Skip to content

Commit

Permalink
chore: kickoff release
Browse files Browse the repository at this point in the history
  • Loading branch information
thisisabhash authored Jul 18, 2024
2 parents 151fac5 + bd5e6bf commit f3bac9c
Show file tree
Hide file tree
Showing 19 changed files with 213,790 additions and 6 deletions.
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@

# Changes to Xcode / OS runtime versions run in CI/CD requires admin approval.
/.github/composite_actions/get_platform_parameters/action.yml @aws-amplify/amplify-ios-admins

# Changes to files in the api-dump or api-dump-test folder require admin approval.
api-dump/* @aws-amplify/amplify-ios-admins
api-dump-test/* @aws-amplify/amplify-ios-admins
223 changes: 223 additions & 0 deletions .github/workflows/api-breaking-changes-detection.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,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 "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')
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.");
}
39 changes: 39 additions & 0 deletions .github/workflows/api_digester_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Swift API Digester Functionality Check

on:
schedule:
- cron: '0 15 * * *' # This will run the action every day at 3:00 pm UTC (8:00 am PDT)
workflow_dispatch: # Allows manual triggering

jobs:
check-swift-api-digester:
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1

- name: Check API Digester
shell: bash
env:
WEBHOOK_URL: ${{ secrets.SLACK_API_CHECKER_WEBHOOK_URL }}
run: |
TEMP_DIR=$(mktemp -d)
echo "Temporary directory created at $TEMP_DIR"
SDK_PATH=$(xcrun --sdk macosx --show-sdk-path)
echo "SDK Path: $SDK_PATH"
# Run swift-api-digester
swift api-digester -sdk "$SDK_PATH" -diagnose-sdk --input-paths api-dump-test/A.json --input-paths api-dump-test/B.json >> "$TEMP_DIR/api-digester-output.txt" 2>&1
# Display the output
cat "$TEMP_DIR/api-digester-output.txt"
if diff "$TEMP_DIR/api-digester-output.txt" api-dump-test/expected-result.txt; then
echo "The output matches the expected result."
else
echo "The output does not match the expected result."
WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
echo "$WORKFLOW_URL" | xargs -I {} curl -s POST "$WEBHOOK_URL" -H "Content-Type:application/json" --data '{"WORKFLOW_URL":"{}"}'
exit 1
fi
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public extension StorageListRequest {
@available(*, deprecated, message: "Use `path` in Storage API instead of `Options`")
public let path: String?

/// The strategy to use when listing contents from subpaths. Defaults to [`.include`](x-source-tag://SubpathStrategy.include)
///
/// - Tag: StorageListRequestOptions.subpathStrategy
public let subpathStrategy: SubpathStrategy

/// Number between 1 and 1,000 that indicates the limit of how many entries to fetch when
/// retreiving file lists from the server.
///
Expand Down Expand Up @@ -94,15 +99,47 @@ public extension StorageListRequest {
public init(accessLevel: StorageAccessLevel = .guest,
targetIdentityId: String? = nil,
path: String? = nil,
subpathStrategy: SubpathStrategy = .include,
pageSize: UInt = 1000,
nextToken: String? = nil,
pluginOptions: Any? = nil) {
self.accessLevel = accessLevel
self.targetIdentityId = targetIdentityId
self.path = path
self.subpathStrategy = subpathStrategy
self.pageSize = pageSize
self.nextToken = nextToken
self.pluginOptions = pluginOptions
}
}
}

public extension StorageListRequest.Options {
/// Represents the strategy used when listing contents from subpaths relative to the given path.
///
/// - Tag: StorageListRequestOptions.SubpathStrategy
enum SubpathStrategy {
/// Items from nested subpaths are included in the results
///
/// - Tag: SubpathStrategy.include
case include

/// Items from nested subpaths are not included in the results. Their subpaths are instead grouped under [`StorageListResult.excludedSubpaths`](StorageListResult.excludedSubpaths).
///
/// - Parameter delimitedBy: The delimiter used to determine subpaths. Defaults to `"/"`
///
/// - SeeAlso: [`StorageListResult.excludedSubpaths`](x-source-tag://StorageListResult.excludedSubpaths)
///
/// - Tag: SubpathStrategy.excludeWithDelimiter
case exclude(delimitedBy: String = "/")

/// Items from nested subpaths are not included in the results. Their subpaths are instead grouped under [`StorageListResult.excludedSubpaths`](StorageListResult.excludedSubpaths).
///
/// - SeeAlso: [`StorageListResult.excludedSubpaths`](x-source-tag://StorageListResult.excludedSubpaths)
///
/// - Tag: SubpathStrategy.exclude
public static var exclude: SubpathStrategy {
return .exclude()
}
}
}
14 changes: 13 additions & 1 deletion Amplify/Categories/Storage/Result/StorageListResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ public struct StorageListResult {
/// [StorageCategoryBehavior.list](x-source-tag://StorageCategoryBehavior.list).
///
/// - Tag: StorageListResult.init
public init(items: [Item], nextToken: String? = nil) {
public init(
items: [Item],
excludedSubpaths: [String] = [],
nextToken: String? = nil
) {
self.items = items
self.excludedSubpaths = excludedSubpaths
self.nextToken = nextToken
}

Expand All @@ -27,6 +32,13 @@ public struct StorageListResult {
/// - Tag: StorageListResult.items
public var items: [Item]


/// Array of excluded subpaths in the Result.
/// This field is only populated when [`StorageListRequest.Options.subpathStrategy`](x-source-tag://StorageListRequestOptions.subpathStragegy) is set to [`.exclude()`](x-source-tag://SubpathStrategy.exclude).
///
/// - Tag: StorageListResult.excludedSubpaths
public var excludedSubpaths: [String]

/// Opaque string indicating the page offset at which to resume a listing. This value is usually copied to
/// [StorageListRequestOptions.nextToken](x-source-tag://StorageListRequestOptions.nextToken).
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ extension AWSS3StorageService {
}
let input = ListObjectsV2Input(bucket: bucket,
continuationToken: options.nextToken,
delimiter: nil,
delimiter: options.subpathStrategy.delimiter,
maxKeys: Int(options.pageSize),
prefix: finalPrefix,
startAfter: nil)
Expand All @@ -41,7 +41,20 @@ extension AWSS3StorageService {
let items = try contents.map {
try StorageListResult.Item(s3Object: $0, prefix: prefix)
}
return StorageListResult(items: items, nextToken: response.nextContinuationToken)

let commonPrefixes = response.commonPrefixes ?? []
let excludedSubpaths: [String] = commonPrefixes.compactMap {
guard let commonPrefix = $0.prefix else {
return nil
}
return String(commonPrefix.dropFirst(prefix.count))
}

return StorageListResult(
items: items,
excludedSubpaths: excludedSubpaths,
nextToken: response.nextContinuationToken
)
} catch let error as StorageErrorConvertible {
throw error.storageError
} catch {
Expand Down
Loading

0 comments on commit f3bac9c

Please sign in to comment.