From 638ffd1e4a8ca98f41a993dacc6c3f6f1811b7ba Mon Sep 17 00:00:00 2001 From: 0marperez Date: Wed, 1 Oct 2025 18:15:29 -0400 Subject: [PATCH 01/20] misc: refactor artifact size metrics --- .../build-and-upload/action.yml | 24 ++++++++++ .../action.yml | 48 ++++++++++++------- .../utils/build-and-upload/cloudwatch.sh | 41 ++++++++++++++++ .../utils/build-and-upload/main.sh | 37 ++++++++++++++ .../utils/build-and-upload/metrics.sh | 14 ++++++ .../utils/download-and-process/compare.sh | 40 ++++++++++++++++ .../utils/download-and-process/main.sh | 15 ++++++ .../actions/artifact-size-metrics/utils/s3.sh | 12 +++++ .../artifact-size-metrics/utils/setup.sh | 5 ++ 9 files changed, 220 insertions(+), 16 deletions(-) create mode 100644 .github/actions/artifact-size-metrics/build-and-upload/action.yml rename .github/actions/artifact-size-metrics/{show-results => download-and-process}/action.yml (71%) create mode 100644 .github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh create mode 100644 .github/actions/artifact-size-metrics/utils/build-and-upload/main.sh create mode 100644 .github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh create mode 100644 .github/actions/artifact-size-metrics/utils/download-and-process/compare.sh create mode 100644 .github/actions/artifact-size-metrics/utils/download-and-process/main.sh create mode 100644 .github/actions/artifact-size-metrics/utils/s3.sh create mode 100644 .github/actions/artifact-size-metrics/utils/setup.sh diff --git a/.github/actions/artifact-size-metrics/build-and-upload/action.yml b/.github/actions/artifact-size-metrics/build-and-upload/action.yml new file mode 100644 index 0000000..d8de9ae --- /dev/null +++ b/.github/actions/artifact-size-metrics/build-and-upload/action.yml @@ -0,0 +1,24 @@ +name: Calculate Artifact Size +description: Calculates size for JVM artifacts + +inputs: + artifact-dirs: + description: List of directories for which artifact sizes will be calculated + required: true + # TODO: Additional inputs + +runs: + using: composite + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Calculate artifact sizes + shell: bash + run: | + chmod +x ./src/main.sh + chmod +x ./src/metrics.sh + chmod +x ./src/s3.sh + chmod +x ./src/cloudwatch.sh + + ./src/main.sh diff --git a/.github/actions/artifact-size-metrics/show-results/action.yml b/.github/actions/artifact-size-metrics/download-and-process/action.yml similarity index 71% rename from .github/actions/artifact-size-metrics/show-results/action.yml rename to .github/actions/artifact-size-metrics/download-and-process/action.yml index 45642f4..ba5a60f 100644 --- a/.github/actions/artifact-size-metrics/show-results/action.yml +++ b/.github/actions/artifact-size-metrics/download-and-process/action.yml @@ -1,25 +1,31 @@ -name: Artifact Size Metrics - Show Results -description: Posts a new artifact analysis comment on the PR +name: Download and process artifact size metrics +description: TODO + inputs: - working-directory: - required: false - description: The working directory to use for reading the previously-generated report. - default: '' + download: + description: TODO + default: "false" + # TODO: Additional inputs + runs: - using: "composite" + using: composite steps: - - name: Post Artifact Analysis Comment + - name: Download and process artifact size metrics + shell: bash + run: | + chmod +x ./src/main.sh + chmod +x ./src/s3.sh + chmod +x ./src/compare.sh + chmod +x ./src/comment.sh + + ./src/main.sh + + - name: Post artifact analysis comment # TODO: MOVE TO SCRIPT IF POSSIBLE uses: actions/github-script@v7 with: script: | - const workingDirectory = '${{ inputs.working-directory }}' - if (workingDirectory) { - process.chdir(workingDirectory) - } - const fs = require('node:fs') - const prNumber = context.issue.number ?? process.env.SDK_PR - + const prNumber = context.issue.number ?? process.env.SDK_PR # TOSO - CHANGE - THIS WON'T WORK FOR EXTERNAL CONTRIBUTORS BTW - IF EXTERNAL CONTRIBUTOR JUST FAIL const prInfo = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, @@ -83,4 +89,14 @@ runs: clientMutationId } }`) - } \ No newline at end of file + } + + - name: Fail if large size increase + shell: bash + if: ${{ !contains(github.event.pull_request.labels.*.name, 'acknowledge-artifact-size-increase') }} + run: | + if [ "$LARGE_DIFF" == "true" ]; then + echo An artifact increased in size by more than allowed or a new artifact was created. + echo If this is expected please add the 'acknowledge-artifact-size-increase' label to this pull request. + exit 1 + fi diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh new file mode 100644 index 0000000..16ecd2f --- /dev/null +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh @@ -0,0 +1,41 @@ +# Upload the artifact size metrics to cloudwatch +uploadToCloudwatch() { + set -o pipefail + + METRICS_FILE="metrics.csv" # path to your CSV file + PROJECT_REPO_NAME=$1 + NAMESPACE="Artifact Size Metrics" + + # Read CSV, skipping header + tail -n +2 "$METRICS_FILE" | while IFS=',' read -r artifactName artifactSize; do + artifactName=$(echo "$artifactName" | xargs) # trim spaces + artifactSize=$(echo "$artifactSize" | xargs) + + # Build JSON for CloudWatch + metric_json=$(jq -n \ + --arg name "$PROJECT_REPO_NAME-$artifactName" \ + --arg value "$artifactSize" \ + --arg project "$PROJECT_REPO_NAME" \ + '{ + MetricName: $name, + Timestamp: (now | todate), + Unit: "Bytes", + Value: ($value | tonumber), + Dimensions: [ + { Name: "Project", Value: $project } + ] + }' + ) + + METRICS+=("$metric_json") + done + + # Send metrics in chunks of 1000 + chunk_size=1000 + for ((i=0; i<${#METRICS[@]}; i+=chunk_size)); do + chunk=("${METRICS[@]:i:chunk_size}") + aws cloudwatch put-metric-data \ + --namespace "$NAMESPACE" \ + --metric-data "$(printf '%s\n' "${chunk[@]}" | jq -s '.')" + done +} diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh new file mode 100644 index 0000000..757848e --- /dev/null +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +source ./metrics.sh +source ./cloudwatch.sh +source ../s3.sh +source ../setup.sh + +setup + +# Build +if [ "$GITHUB_REPOSITORY" = "aws-sdk-kotlin" ]; then + # FIXME: Enable K/N builds + ./gradlew build -Paws.kotlin.native=false build --parallel --max-workers 16 +else + ./gradlew build +fi + +# Move artifacts that will be published to staging dir (build/m2) +./gradlew publish + +# Calculate size for artifacts in staging dir +getArtifactSizes + +# Upload size metrics +if [ "$UPLOAD" == "true" ]; then + if [ "$RELEASE_METRICS" == "true" ]; then + # For record-keeping + uploadToS3 "$GITHUB_REPOSITORY"-v"$IDENTIFIER".csv + uploadToS3 "$GITHUB_REPOSITORY"-latest.csv + + # For display in our OPS dashboard + uploadToCloudwatch "$GITHUB_REPOSITORY" + else + # For downstream consumption in pull requests + uploadToS3 [TEMP]"$GITHUB_REPOSITORY"-pull-request-"$IDENTIFIER".csv + fi +fi diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh new file mode 100644 index 0000000..00b68d0 --- /dev/null +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh @@ -0,0 +1,14 @@ +# Gets artifact size metrics from staging dir +getArtifactSizes() { + output="build/reports/metrics/artifact-size-metrics.csv" + + # Write CSV header + echo "Jar File,Size (Bytes)" > "$output" + + # Find all JARs (exclude sources and javadoc) + # TODO: Calculate KN artifacts sizes + find build/m2 -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | while read -r jar; do + size=$(stat -c%s "$jar") + echo "\"$jar\",$size" >> "$output" + done +} diff --git a/.github/actions/artifact-size-metrics/utils/download-and-process/compare.sh b/.github/actions/artifact-size-metrics/utils/download-and-process/compare.sh new file mode 100644 index 0000000..d158412 --- /dev/null +++ b/.github/actions/artifact-size-metrics/utils/download-and-process/compare.sh @@ -0,0 +1,40 @@ +# Compares two metrics files, saves results into a file, returns "true" if large diff was found +compareMetrics() { + local pr_file="$1" + local release_file="$2" + local output_file="$3" + + # Create header for the Markdown table + echo "| Artifact | Pull Request (bytes) | Latest Release (bytes) | Delta (bytes) | Delta (percentage) |" > "$output_file" + echo "|----------|--------------------|----------------------|---------------|------------------|" >> "$output_file" + + local flag=false + + # Read PR file line by line (skip header) + tail -n +2 "$pr_file" | while IFS=',' read -r artifact pr_size; do + # Trim whitespace + artifact=$(echo "$artifact" | xargs) + pr_size=$(echo "$pr_size" | xargs) + + # Find corresponding artifact in release file + release_size=$(awk -F',' -v art="$artifact" 'NR>1 && $1 ~ art {gsub(/ /,"",$2); print $2}' "$release_file") + + # Skip if artifact not found in release file + [ -z "$release_size" ] && continue + + # Compute delta and percentage + delta=$((pr_size - release_size)) + abs_delta=${delta#-} + percent=$((100 * abs_delta / release_size)) + + # Add row to Markdown table + echo "| $artifact | $pr_size | $release_size | $delta | $percent% |" >> "$output_file" + + # Check if delta > 5% + if [ "$percent" -gt 5 ]; then + flag=true + fi + done + + $flag && echo "true" +} diff --git a/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh b/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh new file mode 100644 index 0000000..2cd2a8f --- /dev/null +++ b/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +source ./compare.sh +source ../s3.sh +source ../setup.sh +setup + +# Get metrics from pull request and from latest release +if [ "$DOWNLOAD" == "true" ]; then + downloadFromS3 [TEMP]"$GITHUB_REPOSITORY"-pull-request-"$IDENTIFIER".csv ./current.csv +fi +downloadFromS3 "$GITHUB_REPOSITORY"-latest.csv ./latest.csv + +# Compare metrics and save if large diff was found +export LARGE_DIFF=$(compareMetrics current.csv latest.csv comparisson.md) diff --git a/.github/actions/artifact-size-metrics/utils/s3.sh b/.github/actions/artifact-size-metrics/utils/s3.sh new file mode 100644 index 0000000..90d7a55 --- /dev/null +++ b/.github/actions/artifact-size-metrics/utils/s3.sh @@ -0,0 +1,12 @@ +# Owned by: aws-kotlin-sdk+ci +S3_ARTIFACT_SIZE_METRICS_BUCKET="artifact-size-metrics" + +# Uploads metrics to the metrics bucket under the specified file name +uploadToS3() { + aws s3 cp "$1" s3://"$S3_ARTIFACT_SIZE_METRICS_BUCKET"/"$2" +} + +# Downloads metrics from the metrics bucket to the specified local file +downloadFromS3() { + aws s3 cp s3://"$S3_ARTIFACT_SIZE_METRICS_BUCKET"/"$1" "$2" +} \ No newline at end of file diff --git a/.github/actions/artifact-size-metrics/utils/setup.sh b/.github/actions/artifact-size-metrics/utils/setup.sh new file mode 100644 index 0000000..2d52fa6 --- /dev/null +++ b/.github/actions/artifact-size-metrics/utils/setup.sh @@ -0,0 +1,5 @@ +setup() { + # Exit if non zero exit code or if env var is missing + set -u + set -e +} From ea8bc0af020f2e8f5369a72353e8606019e774c4 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Wed, 1 Oct 2025 18:49:09 -0400 Subject: [PATCH 02/20] fixes to build-and-upload/main --- .../artifact-size-metrics/utils/build-and-upload/main.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 .github/actions/artifact-size-metrics/utils/build-and-upload/main.sh diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh old mode 100644 new mode 100755 index 757848e..90e2776 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh @@ -1,9 +1,9 @@ #!/bin/bash -source ./metrics.sh -source ./cloudwatch.sh -source ../s3.sh -source ../setup.sh +source "$(dirname "$0")/metrics.sh" +source "$(dirname "$0")/cloudwatch.sh" +source "$(dirname "$0")/../s3.sh" +source "$(dirname "$0")/../setup.sh" setup From 9cb1f3a785c41b18395352699b8830b4a0a848df Mon Sep 17 00:00:00 2001 From: 0marperez Date: Wed, 1 Oct 2025 19:06:12 -0400 Subject: [PATCH 03/20] enable command tracing --- .github/actions/artifact-size-metrics/utils/setup.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/artifact-size-metrics/utils/setup.sh b/.github/actions/artifact-size-metrics/utils/setup.sh index 2d52fa6..27edc2c 100644 --- a/.github/actions/artifact-size-metrics/utils/setup.sh +++ b/.github/actions/artifact-size-metrics/utils/setup.sh @@ -1,5 +1,6 @@ +# Exit if non zero exit code or if env var is missing, and enable command tracing setup() { - # Exit if non zero exit code or if env var is missing set -u set -e + set -x } From 4fb170b7a588251560ace9227a49bbb78139959f Mon Sep 17 00:00:00 2001 From: 0marperez Date: Wed, 1 Oct 2025 19:24:23 -0400 Subject: [PATCH 04/20] fix output file creation --- .../artifact-size-metrics/utils/build-and-upload/metrics.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh index 00b68d0..b7c836d 100644 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh @@ -1,6 +1,9 @@ # Gets artifact size metrics from staging dir getArtifactSizes() { + # Create output file output="build/reports/metrics/artifact-size-metrics.csv" + mkdir -p "$(dirname "$output")" + touch "$output" # Write CSV header echo "Jar File,Size (Bytes)" > "$output" From 2b947670b4aebb5fa110e904d80c448395b57d22 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 2 Oct 2025 12:26:02 -0400 Subject: [PATCH 05/20] rename artifacts --- .../artifact-size-metrics/utils/build-and-upload/metrics.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh index b7c836d..2e1bbde 100644 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh @@ -12,6 +12,8 @@ getArtifactSizes() { # TODO: Calculate KN artifacts sizes find build/m2 -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | while read -r jar; do size=$(stat -c%s "$jar") - echo "\"$jar\",$size" >> "$output" + # strip "-(-timestamp?)" before ".jar" + artifact=$(echo "$jar" | sed -E 's/-[0-9]+(\.[0-9]+)*(-[0-9]{8}\.[0-9]{6}-[0-9]+)?\.jar$/.jar/') + echo "\"$artifact\",$size" >> "$output" done } From 808ea091a072d3ed55ce424acab200fd46c4a956 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 2 Oct 2025 13:11:16 -0400 Subject: [PATCH 06/20] polish metrics collection --- .../utils/build-and-upload/metrics.sh | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh index 2e1bbde..15863eb 100644 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh @@ -1,19 +1,30 @@ # Gets artifact size metrics from staging dir getArtifactSizes() { + # Staging dir + input="build/m2" + # Create output file output="build/reports/metrics/artifact-size-metrics.csv" mkdir -p "$(dirname "$output")" touch "$output" # Write CSV header - echo "Jar File,Size (Bytes)" > "$output" + echo "Artifact, Size (Bytes)" > "$output" # Find all JARs (exclude sources and javadoc) # TODO: Calculate KN artifacts sizes - find build/m2 -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | while read -r jar; do + find "$input" -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | while read -r jar; do size=$(stat -c%s "$jar") - # strip "-(-timestamp?)" before ".jar" - artifact=$(echo "$jar" | sed -E 's/-[0-9]+(\.[0-9]+)*(-[0-9]{8}\.[0-9]{6}-[0-9]+)?\.jar$/.jar/') - echo "\"$artifact\",$size" >> "$output" +# size=$(stat -f%z "$jar") # TODO: Remove when done testing + + # remove dir path, version, optional timestamp, and .jar + artifact=$(basename "$jar") + artifact=$(echo "$artifact" | sed -E 's/-[0-9].*\.jar$//') + + # Add JAR to CSV + echo "$artifact, $size" >> "$output" done + + # Print results for debugging + cat "$output" } From 7dafe57e4a95273a1097b68a6bb2ebf3c3bf4ca5 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 2 Oct 2025 16:27:04 -0400 Subject: [PATCH 07/20] done with build-and-upload --- .../utils/build-and-upload/cloudwatch.sh | 37 ++++++++++--------- .../utils/build-and-upload/main.sh | 13 ++++--- .../utils/build-and-upload/metrics.sh | 24 ++++++------ .../actions/artifact-size-metrics/utils/s3.sh | 2 +- metrics_file | 20 ++++++++++ 5 files changed, 59 insertions(+), 37 deletions(-) create mode 100644 metrics_file diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh index 16ecd2f..ca8459c 100644 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh @@ -1,21 +1,22 @@ # Upload the artifact size metrics to cloudwatch uploadToCloudwatch() { - set -o pipefail + metrics_file="$1" + metrics=() - METRICS_FILE="metrics.csv" # path to your CSV file - PROJECT_REPO_NAME=$1 - NAMESPACE="Artifact Size Metrics" + # Read CSV + while IFS=',' read -r artifactName artifactSize; do + # Skip header + [[ "$artifactName" == "Artifact" ]] && continue - # Read CSV, skipping header - tail -n +2 "$METRICS_FILE" | while IFS=',' read -r artifactName artifactSize; do - artifactName=$(echo "$artifactName" | xargs) # trim spaces + # trim spaces + artifactName=$(echo "$artifactName" | xargs) artifactSize=$(echo "$artifactSize" | xargs) - # Build JSON for CloudWatch - metric_json=$(jq -n \ - --arg name "$PROJECT_REPO_NAME-$artifactName" \ + # Build metric JSON + metrics+=$(jq -n \ + --arg name "$GITHUB_REPOSITORY-$artifactName" \ --arg value "$artifactSize" \ - --arg project "$PROJECT_REPO_NAME" \ + --arg project "$GITHUB_REPOSITORY" \ '{ MetricName: $name, Timestamp: (now | todate), @@ -26,16 +27,16 @@ uploadToCloudwatch() { ] }' ) + done < "$metrics_file" - METRICS+=("$metric_json") - done - - # Send metrics in chunks of 1000 + namespace="Artifact Size Metrics" chunk_size=1000 - for ((i=0; i<${#METRICS[@]}; i+=chunk_size)); do - chunk=("${METRICS[@]:i:chunk_size}") + + # Send metrics in chunks + for ((i=0; i<${#metrics[@]}; i+=chunk_size)); do + chunk=("${metrics[@]:i:chunk_size}") aws cloudwatch put-metric-data \ - --namespace "$NAMESPACE" \ + --namespace "$namespace" \ --metric-data "$(printf '%s\n' "${chunk[@]}" | jq -s '.')" done } diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh index 90e2776..9eb475c 100755 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh @@ -18,20 +18,21 @@ fi # Move artifacts that will be published to staging dir (build/m2) ./gradlew publish -# Calculate size for artifacts in staging dir -getArtifactSizes +# Calculate size for artifacts in staging dir (build/m2) +metrics_file="build/reports/metrics/artifact-size-metrics.csv" +getArtifactSizes metrics_file # Upload size metrics if [ "$UPLOAD" == "true" ]; then if [ "$RELEASE_METRICS" == "true" ]; then # For record-keeping - uploadToS3 "$GITHUB_REPOSITORY"-v"$IDENTIFIER".csv - uploadToS3 "$GITHUB_REPOSITORY"-latest.csv + uploadToS3 "$metrics_file" "$GITHUB_REPOSITORY"-v"$IDENTIFIER".csv + uploadToS3 "$metrics_file" "$GITHUB_REPOSITORY"-latest.csv # For display in our OPS dashboard - uploadToCloudwatch "$GITHUB_REPOSITORY" + uploadToCloudwatch "$metrics_file" "$GITHUB_REPOSITORY" else # For downstream consumption in pull requests - uploadToS3 [TEMP]"$GITHUB_REPOSITORY"-pull-request-"$IDENTIFIER".csv + uploadToS3 "$metrics_file" [TEMP]"$GITHUB_REPOSITORY"-pull-request-"$IDENTIFIER".csv fi fi diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh index 15863eb..f96fceb 100644 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh @@ -1,30 +1,30 @@ # Gets artifact size metrics from staging dir getArtifactSizes() { - # Staging dir - input="build/m2" + # Artifact staging dir + input_dir="build/m2" - # Create output file - output="build/reports/metrics/artifact-size-metrics.csv" - mkdir -p "$(dirname "$output")" - touch "$output" + # Create output_file file + output_file="$1" + mkdir -p "$(dirname "$output_file")" + touch "$output_file" # Write CSV header - echo "Artifact, Size (Bytes)" > "$output" + echo "Artifact, Size (Bytes)" > "$output_file" # Find all JARs (exclude sources and javadoc) # TODO: Calculate KN artifacts sizes - find "$input" -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | while read -r jar; do - size=$(stat -c%s "$jar") -# size=$(stat -f%z "$jar") # TODO: Remove when done testing + find "$input_dir" -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | while read -r jar; do +# size=$(stat -c%s "$jar") + size=$(stat -f%z "$jar") # TODO: Remove when done testing # remove dir path, version, optional timestamp, and .jar artifact=$(basename "$jar") artifact=$(echo "$artifact" | sed -E 's/-[0-9].*\.jar$//') # Add JAR to CSV - echo "$artifact, $size" >> "$output" + echo "$artifact, $size" >> "$output_file" done # Print results for debugging - cat "$output" + cat "$output_file" } diff --git a/.github/actions/artifact-size-metrics/utils/s3.sh b/.github/actions/artifact-size-metrics/utils/s3.sh index 90d7a55..eadc99d 100644 --- a/.github/actions/artifact-size-metrics/utils/s3.sh +++ b/.github/actions/artifact-size-metrics/utils/s3.sh @@ -1,5 +1,5 @@ # Owned by: aws-kotlin-sdk+ci -S3_ARTIFACT_SIZE_METRICS_BUCKET="artifact-size-metrics" +S3_ARTIFACT_SIZE_METRICS_BUCKET="artifact-size-metrics-2" # TODO: Remove "-2" when done testing # Uploads metrics to the metrics bucket under the specified file name uploadToS3() { diff --git a/metrics_file b/metrics_file new file mode 100644 index 0000000..3fe02b8 --- /dev/null +++ b/metrics_file @@ -0,0 +1,20 @@ +Artifact, Size (Bytes) +aws-core, 647 +hll-mapping-core, 647 +lambda-jvm, 3851286 +aws-endpoint-jvm, 10966 +hll-codegen, 120718 +aws-core-jvm, 5980 +style-rules, 13183 +kmp-conventions, 30972 +ktlint-rules, 9590 +minor-version-rules, 6399 +smithy-build, 38428 +build-support, 162657 +aws-http, 647 +lambda, 647 +aws-endpoint, 647 +aws-config, 647 +aws-config-jvm, 1044789 +aws-http-jvm, 73207 +hll-mapping-core-jvm, 38175 From 38f2890abcbac21d54568f6d2250475513d273a7 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 2 Oct 2025 16:33:44 -0400 Subject: [PATCH 08/20] fix file creation bug --- .../utils/build-and-upload/main.sh | 2 +- metrics_file | 20 ------------------- 2 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 metrics_file diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh index 9eb475c..1a4c7e2 100755 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh @@ -20,7 +20,7 @@ fi # Calculate size for artifacts in staging dir (build/m2) metrics_file="build/reports/metrics/artifact-size-metrics.csv" -getArtifactSizes metrics_file +getArtifactSizes $metrics_file # Upload size metrics if [ "$UPLOAD" == "true" ]; then diff --git a/metrics_file b/metrics_file deleted file mode 100644 index 3fe02b8..0000000 --- a/metrics_file +++ /dev/null @@ -1,20 +0,0 @@ -Artifact, Size (Bytes) -aws-core, 647 -hll-mapping-core, 647 -lambda-jvm, 3851286 -aws-endpoint-jvm, 10966 -hll-codegen, 120718 -aws-core-jvm, 5980 -style-rules, 13183 -kmp-conventions, 30972 -ktlint-rules, 9590 -minor-version-rules, 6399 -smithy-build, 38428 -build-support, 162657 -aws-http, 647 -lambda, 647 -aws-endpoint, 647 -aws-config, 647 -aws-config-jvm, 1044789 -aws-http-jvm, 73207 -hll-mapping-core-jvm, 38175 From bd7e141c9bbb39a14cc905d1800a8018b0d78755 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 2 Oct 2025 17:40:44 -0400 Subject: [PATCH 09/20] done with scipts? --- .../build-and-upload/action.yml | 4 - .../{metrics.sh => calculate_metrics.sh} | 6 +- .../utils/build-and-upload/cloudwatch.sh | 2 +- .../utils/build-and-upload/main.sh | 22 ++--- .../artifact-size-metrics/utils/constants.sh | 11 +++ .../utils/download-and-process/compare.sh | 86 +++++++++++-------- .../utils/download-and-process/main.sh | 24 ++++-- .../actions/artifact-size-metrics/utils/s3.sh | 4 +- 8 files changed, 93 insertions(+), 66 deletions(-) rename .github/actions/artifact-size-metrics/utils/build-and-upload/{metrics.sh => calculate_metrics.sh} (90%) create mode 100644 .github/actions/artifact-size-metrics/utils/constants.sh mode change 100644 => 100755 .github/actions/artifact-size-metrics/utils/download-and-process/main.sh diff --git a/.github/actions/artifact-size-metrics/build-and-upload/action.yml b/.github/actions/artifact-size-metrics/build-and-upload/action.yml index d8de9ae..99f2c99 100644 --- a/.github/actions/artifact-size-metrics/build-and-upload/action.yml +++ b/.github/actions/artifact-size-metrics/build-and-upload/action.yml @@ -17,8 +17,4 @@ runs: shell: bash run: | chmod +x ./src/main.sh - chmod +x ./src/metrics.sh - chmod +x ./src/s3.sh - chmod +x ./src/cloudwatch.sh - ./src/main.sh diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh similarity index 90% rename from .github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh rename to .github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh index f96fceb..4f513d1 100644 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/metrics.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh @@ -1,9 +1,9 @@ # Gets artifact size metrics from staging dir -getArtifactSizes() { +calculateArtifactSizes() { # Artifact staging dir input_dir="build/m2" - # Create output_file file + # Create output_file output_file="$1" mkdir -p "$(dirname "$output_file")" touch "$output_file" @@ -21,7 +21,7 @@ getArtifactSizes() { artifact=$(basename "$jar") artifact=$(echo "$artifact" | sed -E 's/-[0-9].*\.jar$//') - # Add JAR to CSV + # Add artifact size to CSV echo "$artifact, $size" >> "$output_file" done diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh index ca8459c..f9bc523 100644 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh @@ -8,7 +8,7 @@ uploadToCloudwatch() { # Skip header [[ "$artifactName" == "Artifact" ]] && continue - # trim spaces + # Trim spaces artifactName=$(echo "$artifactName" | xargs) artifactSize=$(echo "$artifactSize" | xargs) diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh index 1a4c7e2..48955d5 100755 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh @@ -1,8 +1,11 @@ #!/bin/bash -source "$(dirname "$0")/metrics.sh" +# Bash script to build, calculate, and upload artifact size metrics + +source "$(dirname "$0")/calculate_metrics.sh" source "$(dirname "$0")/cloudwatch.sh" source "$(dirname "$0")/../s3.sh" +source "$(dirname "$0")/../constants.sh" source "$(dirname "$0")/../setup.sh" setup @@ -15,24 +18,23 @@ else ./gradlew build fi -# Move artifacts that will be published to staging dir (build/m2) -./gradlew publish +# Move artifacts that'll be published to staging dir (build/m2) +./gradlew publish --parallel --max-workers 16 -# Calculate size for artifacts in staging dir (build/m2) -metrics_file="build/reports/metrics/artifact-size-metrics.csv" -getArtifactSizes $metrics_file +# Calculate size for artifacts in staging dir (build/m2) and save them to metrics_file +calculateArtifactSizes "$metrics_file" # see: constants.sh -# Upload size metrics +# Upload metrics to S3/cloudwatch if required if [ "$UPLOAD" == "true" ]; then if [ "$RELEASE_METRICS" == "true" ]; then # For record-keeping - uploadToS3 "$metrics_file" "$GITHUB_REPOSITORY"-v"$IDENTIFIER".csv - uploadToS3 "$metrics_file" "$GITHUB_REPOSITORY"-latest.csv + uploadToMetricsBucket "$metrics_file" "$GITHUB_REPOSITORY"-v"$IDENTIFIER".csv + uploadToMetricsBucket "$metrics_file" "$GITHUB_REPOSITORY"-latest.csv # For display in our OPS dashboard uploadToCloudwatch "$metrics_file" "$GITHUB_REPOSITORY" else # For downstream consumption in pull requests - uploadToS3 "$metrics_file" [TEMP]"$GITHUB_REPOSITORY"-pull-request-"$IDENTIFIER".csv + uploadToMetricsBucket "$metrics_file" [TEMP]"$GITHUB_REPOSITORY"-pull-request-"$IDENTIFIER".csv fi fi diff --git a/.github/actions/artifact-size-metrics/utils/constants.sh b/.github/actions/artifact-size-metrics/utils/constants.sh new file mode 100644 index 0000000..91e8230 --- /dev/null +++ b/.github/actions/artifact-size-metrics/utils/constants.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Where current metrics are stored +metrics_file="build/reports/metrics/artifact-size-metrics.csv" + +# Where metrics from latest release are stored +latest_release_metrics_file="build/reports/metrics/latest-release-artifact-size-metrics.csv" + +# Where the metrics comparison results are stored +metrics_comparison_file="build/reports/metrics/comparison.md" + diff --git a/.github/actions/artifact-size-metrics/utils/download-and-process/compare.sh b/.github/actions/artifact-size-metrics/utils/download-and-process/compare.sh index d158412..81d953b 100644 --- a/.github/actions/artifact-size-metrics/utils/download-and-process/compare.sh +++ b/.github/actions/artifact-size-metrics/utils/download-and-process/compare.sh @@ -1,40 +1,50 @@ -# Compares two metrics files, saves results into a file, returns "true" if large diff was found +# Compares artifact size metrics to ones from the latest available release, +# stores comparison as a markdown table, +# and returns "true" if a large diff was found (over 5%) compareMetrics() { - local pr_file="$1" - local release_file="$2" - local output_file="$3" - - # Create header for the Markdown table - echo "| Artifact | Pull Request (bytes) | Latest Release (bytes) | Delta (bytes) | Delta (percentage) |" > "$output_file" - echo "|----------|--------------------|----------------------|---------------|------------------|" >> "$output_file" - - local flag=false - - # Read PR file line by line (skip header) - tail -n +2 "$pr_file" | while IFS=',' read -r artifact pr_size; do - # Trim whitespace - artifact=$(echo "$artifact" | xargs) - pr_size=$(echo "$pr_size" | xargs) - - # Find corresponding artifact in release file - release_size=$(awk -F',' -v art="$artifact" 'NR>1 && $1 ~ art {gsub(/ /,"",$2); print $2}' "$release_file") - - # Skip if artifact not found in release file - [ -z "$release_size" ] && continue - - # Compute delta and percentage - delta=$((pr_size - release_size)) - abs_delta=${delta#-} - percent=$((100 * abs_delta / release_size)) - - # Add row to Markdown table - echo "| $artifact | $pr_size | $release_size | $delta | $percent% |" >> "$output_file" - - # Check if delta > 5% - if [ "$percent" -gt 5 ]; then - flag=true - fi - done - - $flag && echo "true" + local metrics_file="$1" + local latest_release_metrics_file="$2" + local metrics_comparison_file="$3" + + # Title and table headers + { + echo "Affected Artifacts" + echo "=" + echo "| Artifact | Pull Request (bytes) | Latest Release (bytes) | Delta (bytes) | Delta (percentage) |" + echo "|----------|----------------------|------------------------|---------------|--------------------|" + } > "$metrics_comparison_file" + + large_diff=false + + # Read CSV + while IFS=',' read -r artifact size; do + # Skip header + [ "$artifact" = "Artifact" ] && continue + + # Trim spaces + artifact=$(echo "$artifact" | xargs) + size=$(echo "$size" | xargs) + + # Find corresponding artifact size in release file or skip + latest_release_size=$(awk -F',' -v art="$artifact" 'NR>1 && $1==art {gsub(/ /,"",$2); print $2}' "$latest_release_metrics_file") + [ -z "$latest_release_size" ] && continue + + # Find delta + delta=$((size - latest_release_size)) + abs_delta=${delta#-} + percent=$((100 * abs_delta / latest_release_size)) + + # Add to file + echo "| $artifact | $size | $latest_release_size | $delta | ${percent}% |" >> "$metrics_comparison_file" + + # Check for large diff + if [ "$percent" -gt 5 ]; then + large_diff=true + fi + done < "$metrics_file" + + # Print results for debugging + cat "$metrics_comparison_file" + + $large_diff && echo "true" } diff --git a/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh b/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh old mode 100644 new mode 100755 index 2cd2a8f..d36304c --- a/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh +++ b/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh @@ -1,15 +1,23 @@ #!/bin/bash -source ./compare.sh -source ../s3.sh -source ../setup.sh +source "$(dirname "$0")/compare.sh" +source "$(dirname "$0")/../s3.sh" +source "$(dirname "$0")/../constants.sh" +source "$(dirname "$0")/../setup.sh" + setup -# Get metrics from pull request and from latest release if [ "$DOWNLOAD" == "true" ]; then - downloadFromS3 [TEMP]"$GITHUB_REPOSITORY"-pull-request-"$IDENTIFIER".csv ./current.csv + # Get metrics calculated in codebuild - otherwise metrics will already be here + downloadFromMetricsBucket [TEMP]"$GITHUB_REPOSITORY"-pull-request-"$IDENTIFIER".csv "$metrics_file" # see: constants.sh fi -downloadFromS3 "$GITHUB_REPOSITORY"-latest.csv ./latest.csv -# Compare metrics and save if large diff was found -export LARGE_DIFF=$(compareMetrics current.csv latest.csv comparisson.md) +# Metrics from the latest release are never available so we need to download them every time +downloadFromMetricsBucket "$GITHUB_REPOSITORY"-latest.csv "$latest_release_metrics_file" # see: constants.sh + +# Compare metrics +export LARGE_DIFF=$(compareMetrics "$metrics_file" "$latest_release_metrics_file" "$metrics_comparison_file") # see: constants.sh + +if [ "$LARGE_DIFF" == "true" ]; then + echo "Large diff found!" +fi diff --git a/.github/actions/artifact-size-metrics/utils/s3.sh b/.github/actions/artifact-size-metrics/utils/s3.sh index eadc99d..861329e 100644 --- a/.github/actions/artifact-size-metrics/utils/s3.sh +++ b/.github/actions/artifact-size-metrics/utils/s3.sh @@ -2,11 +2,11 @@ S3_ARTIFACT_SIZE_METRICS_BUCKET="artifact-size-metrics-2" # TODO: Remove "-2" when done testing # Uploads metrics to the metrics bucket under the specified file name -uploadToS3() { +uploadToMetricsBucket() { aws s3 cp "$1" s3://"$S3_ARTIFACT_SIZE_METRICS_BUCKET"/"$2" } # Downloads metrics from the metrics bucket to the specified local file -downloadFromS3() { +downloadFromMetricsBucket() { aws s3 cp s3://"$S3_ARTIFACT_SIZE_METRICS_BUCKET"/"$1" "$2" } \ No newline at end of file From 771898cef2a69f204a5840834bb2e273ab099cd6 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 2 Oct 2025 17:44:25 -0400 Subject: [PATCH 10/20] quick self review --- .github/actions/artifact-size-metrics/utils/constants.sh | 1 - .../artifact-size-metrics/utils/download-and-process/main.sh | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/actions/artifact-size-metrics/utils/constants.sh b/.github/actions/artifact-size-metrics/utils/constants.sh index 91e8230..183d66d 100644 --- a/.github/actions/artifact-size-metrics/utils/constants.sh +++ b/.github/actions/artifact-size-metrics/utils/constants.sh @@ -8,4 +8,3 @@ latest_release_metrics_file="build/reports/metrics/latest-release-artifact-size- # Where the metrics comparison results are stored metrics_comparison_file="build/reports/metrics/comparison.md" - diff --git a/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh b/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh index d36304c..acd5675 100755 --- a/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh +++ b/.github/actions/artifact-size-metrics/utils/download-and-process/main.sh @@ -1,5 +1,7 @@ #!/bin/bash +# Bash script to download, and compare artifact size metrics + source "$(dirname "$0")/compare.sh" source "$(dirname "$0")/../s3.sh" source "$(dirname "$0")/../constants.sh" @@ -12,7 +14,7 @@ if [ "$DOWNLOAD" == "true" ]; then downloadFromMetricsBucket [TEMP]"$GITHUB_REPOSITORY"-pull-request-"$IDENTIFIER".csv "$metrics_file" # see: constants.sh fi -# Metrics from the latest release are never available so we need to download them every time +# Metrics from the latest release are never calculated here so we need to download them downloadFromMetricsBucket "$GITHUB_REPOSITORY"-latest.csv "$latest_release_metrics_file" # see: constants.sh # Compare metrics From bbf999ad618c97eeb561f2b100626be7234d82b5 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 2 Oct 2025 18:01:29 -0400 Subject: [PATCH 11/20] setup for remote testing --- .../utils/build-and-upload/calculate_metrics.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh index 4f513d1..3813a2c 100644 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh @@ -14,8 +14,8 @@ calculateArtifactSizes() { # Find all JARs (exclude sources and javadoc) # TODO: Calculate KN artifacts sizes find "$input_dir" -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | while read -r jar; do -# size=$(stat -c%s "$jar") - size=$(stat -f%z "$jar") # TODO: Remove when done testing + size=$(stat -c%s "$jar") +# size=$(stat -f%z "$jar") # TODO: Remove when done testing # remove dir path, version, optional timestamp, and .jar artifact=$(basename "$jar") From 090719299f77d16b9fcae194eca29caaafa6fb73 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 2 Oct 2025 19:33:16 -0400 Subject: [PATCH 12/20] changes to actions --- .../build-and-upload/action.yml | 20 +-- .../download-and-process/action.yml | 120 ++++++------------ 2 files changed, 53 insertions(+), 87 deletions(-) diff --git a/.github/actions/artifact-size-metrics/build-and-upload/action.yml b/.github/actions/artifact-size-metrics/build-and-upload/action.yml index 99f2c99..3b23f8b 100644 --- a/.github/actions/artifact-size-metrics/build-and-upload/action.yml +++ b/.github/actions/artifact-size-metrics/build-and-upload/action.yml @@ -2,19 +2,21 @@ name: Calculate Artifact Size description: Calculates size for JVM artifacts inputs: - artifact-dirs: - description: List of directories for which artifact sizes will be calculated - required: true - # TODO: Additional inputs + upload: + description: Whether the metrics should be uploaded to S3/Cloudwatch + release_metrics: + description: Whether the metrics are coming from a release build runs: using: composite steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Calculate artifact sizes shell: bash + env: + GITHUB_REPOSITORY: ${{ github.repository }} + IDENTIFIER: ${{ github.ref_name }} + UPLOAD: ${{ inputs.upload }} + RELEASE_METRICS: ${{ inputs.release_metrics }} run: | - chmod +x ./src/main.sh - ./src/main.sh + chmod +x ../utils/build-and-upload/main.sh + ../utils/build-and-upload/main.sh diff --git a/.github/actions/artifact-size-metrics/download-and-process/action.yml b/.github/actions/artifact-size-metrics/download-and-process/action.yml index ba5a60f..074a69b 100644 --- a/.github/actions/artifact-size-metrics/download-and-process/action.yml +++ b/.github/actions/artifact-size-metrics/download-and-process/action.yml @@ -1,102 +1,66 @@ -name: Download and process artifact size metrics -description: TODO +name: Process artifact size metrics +description: Compares artifact size metrics, leaves a comment, and fails if a size increase of ≥5% is detected. inputs: download: - description: TODO - default: "false" - # TODO: Additional inputs + description: Whether the artifact size metrics should be downloaded from S3 runs: using: composite steps: - name: Download and process artifact size metrics + description: Compares artifact size metrics and sets LARGE_DIFF to true if a size increase of ≥5% is detected. shell: bash + env: + DOWNLOAD: ${{ inputs.download }} + GITHUB_REPOSITORY: ${{ github.repository }} + IDENTIFIER: ${{ github.ref_name }} run: | - chmod +x ./src/main.sh - chmod +x ./src/s3.sh - chmod +x ./src/compare.sh - chmod +x ./src/comment.sh - - ./src/main.sh + chmod +x ../utils/download-and-process/main.sh + ../utils/download-and-process/main.sh - - name: Post artifact analysis comment # TODO: MOVE TO SCRIPT IF POSSIBLE + - name: Comment artifact size metrics comparison + if: github.event_name == 'pull_request' uses: actions/github-script@v7 with: script: | - const fs = require('node:fs') - const prNumber = context.issue.number ?? process.env.SDK_PR # TOSO - CHANGE - THIS WON'T WORK FOR EXTERNAL CONTRIBUTORS BTW - IF EXTERNAL CONTRIBUTOR JUST FAIL - const prInfo = await github.rest.pulls.get({ + const fs = require('node:fs') + + // Read the artifact size metrics comparison + const comparison = fs.readFileSync('build/reports/metrics/artifact-analysis.md', 'utf8') + + // Get PR ID + const { data: pullRequest } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, - pull_number: prNumber + pull_number: context.issue.number }) - - const hasAcknowledgeLabel = prInfo.data.labels.some(label => label.name === 'acknowledge-artifact-size-increase') - - const getComments = ` - query { - repository(owner:"${context.repo.owner}", name:"${context.repo.repo}"){ - pullRequest(number: ${prNumber}) { - id - comments(last:100) { - nodes { - id - body - author { - login - } - isMinimized - } - } - } - } - }` - - const response = await github.graphql(getComments) - const comments = response.repository.pullRequest.comments.nodes - - // Minimize outdated artifact-size comments - const mutations = comments - .filter(comment => comment.author.login == 'github-actions' && !comment.isMinimized && comment.body.startsWith('Affected Artifacts')) - .map(comment => - github.graphql(`mutation { - minimizeComment(input:{subjectId:"${comment.id}", classifier:OUTDATED}){ - clientMutationId - } - }`) - ) - await Promise.all(mutations) - - const comment = fs.readFileSync('build/reports/metrics/artifact-analysis.md', 'utf8') - - // Create a new comment with the latest artifact analysis - const writeComment = `mutation { - addComment(input:{body:"""${comment}""", subjectId:"${response.repository.pullRequest.id}"}){ - commentEdge { - node { - id - } - } - }}` - const addCommentResponse = await github.graphql(writeComment) - const newCommentId = addCommentResponse.addComment.commentEdge.node.id - - // Minimize the newly-created comment if artifact size increase is acknowledged - if (hasAcknowledgeLabel) { - await github.graphql(`mutation { - minimizeComment(input:{subjectId:"${newCommentId}", classifier:RESOLVED}){ - clientMutationId + const pullRequestId = pullRequest.node_id + + // Add the comment + const addCommentResponse = await github.graphql(` + mutation { + addComment(input: {body: """${comparison}""", subjectId: "${pullRequestId}"}) { + commentEdge { node { id } } } - }`) - } + } + `) + + const commentId = addCommentResponse.addComment.commentEdge.node.id + + // Minimize the comment + await github.graphql(` + mutation { + minimizeComment(input: {subjectId: "${commentId}", classifier: RESOLVED}) { } + } + `) - - name: Fail if large size increase - shell: bash + - name: Large size increase? if: ${{ !contains(github.event.pull_request.labels.*.name, 'acknowledge-artifact-size-increase') }} + shell: bash run: | if [ "$LARGE_DIFF" == "true" ]; then - echo An artifact increased in size by more than allowed or a new artifact was created. - echo If this is expected please add the 'acknowledge-artifact-size-increase' label to this pull request. + echo "An artifact has increased in size by more than 5%. + If this is expected, please add the acknowledge-artifact-size-increase label to this pull request." exit 1 fi From af71947c0e2a6c5c3a5250765b0d4f38cac65305 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Fri, 3 Oct 2025 11:09:10 -0400 Subject: [PATCH 13/20] disable kotlin native for publish --- .../artifact-size-metrics/utils/build-and-upload/main.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh index 48955d5..8672db9 100755 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh @@ -10,17 +10,16 @@ source "$(dirname "$0")/../setup.sh" setup -# Build +# Build and move artifacts that'll be published to staging dir (build/m2) if [ "$GITHUB_REPOSITORY" = "aws-sdk-kotlin" ]; then # FIXME: Enable K/N builds ./gradlew build -Paws.kotlin.native=false build --parallel --max-workers 16 + ./gradlew -Paws.kotlin.native=false publish --parallel --max-workers 16 else ./gradlew build + ./gradlew publish --parallel --max-workers 16 fi -# Move artifacts that'll be published to staging dir (build/m2) -./gradlew publish --parallel --max-workers 16 - # Calculate size for artifacts in staging dir (build/m2) and save them to metrics_file calculateArtifactSizes "$metrics_file" # see: constants.sh From 7c77b82cc3786f23cd7e322d983f6e0cc5a4abb6 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Fri, 3 Oct 2025 11:34:55 -0400 Subject: [PATCH 14/20] get ready for review --- .../utils/build-and-upload/calculate_metrics.sh | 1 - .github/actions/artifact-size-metrics/utils/s3.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh b/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh index 3813a2c..24b8973 100644 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh +++ b/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh @@ -15,7 +15,6 @@ calculateArtifactSizes() { # TODO: Calculate KN artifacts sizes find "$input_dir" -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | while read -r jar; do size=$(stat -c%s "$jar") -# size=$(stat -f%z "$jar") # TODO: Remove when done testing # remove dir path, version, optional timestamp, and .jar artifact=$(basename "$jar") diff --git a/.github/actions/artifact-size-metrics/utils/s3.sh b/.github/actions/artifact-size-metrics/utils/s3.sh index 861329e..49fdc51 100644 --- a/.github/actions/artifact-size-metrics/utils/s3.sh +++ b/.github/actions/artifact-size-metrics/utils/s3.sh @@ -1,5 +1,5 @@ # Owned by: aws-kotlin-sdk+ci -S3_ARTIFACT_SIZE_METRICS_BUCKET="artifact-size-metrics-2" # TODO: Remove "-2" when done testing +S3_ARTIFACT_SIZE_METRICS_BUCKET="artifact-size-metrics" # Uploads metrics to the metrics bucket under the specified file name uploadToMetricsBucket() { From 46ce31868f44463453ea5e2114b4165d03947416 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Fri, 3 Oct 2025 15:03:09 -0400 Subject: [PATCH 15/20] feedback --- .../action.yml | 8 +- .../download-and-process/action.yml | 1 + .../calculate_metrics.sh | 0 .../cloudwatch.sh | 0 .../main.sh | 12 +- build-plugins/build-support/build.gradle.kts | 9 - .../AnalyzeArtifactSizeMetrics.kt | 209 ------------------ .../ArtifactSizeMetricsPlugin.kt | 127 ----------- .../CollectArtifactSizeMetrics.kt | 67 ------ .../CollectDelegatedArtifactSizeMetrics.kt | 97 -------- .../PutArtifactSizeMetricsInCloudWatch.kt | 76 ------- .../SaveArtifactSizeMetrics.kt | 55 ----- 12 files changed, 7 insertions(+), 654 deletions(-) rename .github/actions/artifact-size-metrics/{build-and-upload => calculate-and-upload}/action.yml (73%) rename .github/actions/artifact-size-metrics/utils/{build-and-upload => calculate-and-upload}/calculate_metrics.sh (100%) rename .github/actions/artifact-size-metrics/utils/{build-and-upload => calculate-and-upload}/cloudwatch.sh (100%) rename .github/actions/artifact-size-metrics/utils/{build-and-upload => calculate-and-upload}/main.sh (66%) delete mode 100644 build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/AnalyzeArtifactSizeMetrics.kt delete mode 100644 build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/ArtifactSizeMetricsPlugin.kt delete mode 100644 build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/CollectArtifactSizeMetrics.kt delete mode 100644 build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/CollectDelegatedArtifactSizeMetrics.kt delete mode 100644 build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/PutArtifactSizeMetricsInCloudWatch.kt delete mode 100644 build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/SaveArtifactSizeMetrics.kt diff --git a/.github/actions/artifact-size-metrics/build-and-upload/action.yml b/.github/actions/artifact-size-metrics/calculate-and-upload/action.yml similarity index 73% rename from .github/actions/artifact-size-metrics/build-and-upload/action.yml rename to .github/actions/artifact-size-metrics/calculate-and-upload/action.yml index 3b23f8b..0761ab7 100644 --- a/.github/actions/artifact-size-metrics/build-and-upload/action.yml +++ b/.github/actions/artifact-size-metrics/calculate-and-upload/action.yml @@ -4,13 +4,15 @@ description: Calculates size for JVM artifacts inputs: upload: description: Whether the metrics should be uploaded to S3/Cloudwatch + type: boolean release_metrics: description: Whether the metrics are coming from a release build + type: boolean runs: using: composite steps: - - name: Calculate artifact sizes + - name: Calculate and upload artifact sizes shell: bash env: GITHUB_REPOSITORY: ${{ github.repository }} @@ -18,5 +20,5 @@ runs: UPLOAD: ${{ inputs.upload }} RELEASE_METRICS: ${{ inputs.release_metrics }} run: | - chmod +x ../utils/build-and-upload/main.sh - ../utils/build-and-upload/main.sh + chmod +x ../utils/calculate-and-upload/main.sh + ../utils/calculate-and-upload/main.sh diff --git a/.github/actions/artifact-size-metrics/download-and-process/action.yml b/.github/actions/artifact-size-metrics/download-and-process/action.yml index 074a69b..3de5b9a 100644 --- a/.github/actions/artifact-size-metrics/download-and-process/action.yml +++ b/.github/actions/artifact-size-metrics/download-and-process/action.yml @@ -4,6 +4,7 @@ description: Compares artifact size metrics, leaves a comment, and fails if a si inputs: download: description: Whether the artifact size metrics should be downloaded from S3 + type: boolean runs: using: composite diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh b/.github/actions/artifact-size-metrics/utils/calculate-and-upload/calculate_metrics.sh similarity index 100% rename from .github/actions/artifact-size-metrics/utils/build-and-upload/calculate_metrics.sh rename to .github/actions/artifact-size-metrics/utils/calculate-and-upload/calculate_metrics.sh diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh b/.github/actions/artifact-size-metrics/utils/calculate-and-upload/cloudwatch.sh similarity index 100% rename from .github/actions/artifact-size-metrics/utils/build-and-upload/cloudwatch.sh rename to .github/actions/artifact-size-metrics/utils/calculate-and-upload/cloudwatch.sh diff --git a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh b/.github/actions/artifact-size-metrics/utils/calculate-and-upload/main.sh similarity index 66% rename from .github/actions/artifact-size-metrics/utils/build-and-upload/main.sh rename to .github/actions/artifact-size-metrics/utils/calculate-and-upload/main.sh index 8672db9..76007b9 100755 --- a/.github/actions/artifact-size-metrics/utils/build-and-upload/main.sh +++ b/.github/actions/artifact-size-metrics/utils/calculate-and-upload/main.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Bash script to build, calculate, and upload artifact size metrics +# Bash script to calculate, and upload artifact size metrics source "$(dirname "$0")/calculate_metrics.sh" source "$(dirname "$0")/cloudwatch.sh" @@ -10,16 +10,6 @@ source "$(dirname "$0")/../setup.sh" setup -# Build and move artifacts that'll be published to staging dir (build/m2) -if [ "$GITHUB_REPOSITORY" = "aws-sdk-kotlin" ]; then - # FIXME: Enable K/N builds - ./gradlew build -Paws.kotlin.native=false build --parallel --max-workers 16 - ./gradlew -Paws.kotlin.native=false publish --parallel --max-workers 16 -else - ./gradlew build - ./gradlew publish --parallel --max-workers 16 -fi - # Calculate size for artifacts in staging dir (build/m2) and save them to metrics_file calculateArtifactSizes "$metrics_file" # see: constants.sh diff --git a/build-plugins/build-support/build.gradle.kts b/build-plugins/build-support/build.gradle.kts index 2c8de49..5afc1b2 100644 --- a/build-plugins/build-support/build.gradle.kts +++ b/build-plugins/build-support/build.gradle.kts @@ -27,15 +27,6 @@ dependencies { testImplementation(libs.kotlinx.coroutines.test) } -gradlePlugin { - plugins { - create("artifact-size-metrics") { - id = "aws.sdk.kotlin.gradle.artifactsizemetrics" - implementationClass = "aws.sdk.kotlin.gradle.plugins.artifactsizemetrics.ArtifactSizeMetricsPlugin" - } - } -} - val generateKtlintVersion by tasks.registering { // generate the version of the runtime to use as a resource. // this keeps us from having to manually change version numbers in multiple places diff --git a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/AnalyzeArtifactSizeMetrics.kt b/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/AnalyzeArtifactSizeMetrics.kt deleted file mode 100644 index 2f76476..0000000 --- a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/AnalyzeArtifactSizeMetrics.kt +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package aws.sdk.kotlin.gradle.plugins.artifactsizemetrics - -import aws.sdk.kotlin.services.s3.S3Client -import aws.sdk.kotlin.services.s3.model.GetObjectRequest -import aws.smithy.kotlin.runtime.content.decodeToString -import aws.smithy.kotlin.runtime.io.use -import kotlinx.coroutines.runBlocking -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import java.io.File -import kotlin.math.abs - -/** - * Gradle task that analyzes/compares a project's local artifact size metrics to - * ones from a project's latest GitHub release. Outputs the results into various files. - */ -internal abstract class AnalyzeArtifactSizeMetrics : DefaultTask() { - /** - * File containing the project's current computed artifact size metrics. - */ - @get:InputFile - abstract val metricsFile: RegularFileProperty - - /** - * File containing the results of analyzing the artifact size metrics. - */ - @get:OutputFile - abstract val analysisFile: RegularFileProperty - - /** - * File containing either "true" or "false". - */ - @get:OutputFile - abstract val hasSignificantChangeFile: RegularFileProperty - - init { - metricsFile.convention(project.layout.buildDirectory.file(OUTPUT_PATH + "artifact-size-metrics.csv")) - analysisFile.convention(project.layout.buildDirectory.file(OUTPUT_PATH + "artifact-analysis.md")) - hasSignificantChangeFile.convention(project.layout.buildDirectory.file(OUTPUT_PATH + "has-significant-change.txt")) - } - - private val pluginConfig = project.rootProject.extensions.getByType(ArtifactSizeMetricsPluginConfig::class.java) - - @TaskAction - fun analyze() { - val latestReleaseMetricsFile = - project.layout.buildDirectory.file(OUTPUT_PATH + "latest-release-artifact-size-metrics.csv").get().asFile - writeLatestReleaseMetrics(latestReleaseMetricsFile) - - val latestReleaseMetrics = latestReleaseMetricsFile.toMap() - val currentMetrics = metricsFile.get().asFile.toMap() - val analysis = analyzeArtifactSizeMetrics(latestReleaseMetrics, currentMetrics) - - hasSignificantChangeFile.get().asFile.writeText(analysis.significantChange.toString()) - val diffTables = createDiffTables(analysis) - val output = if (analysis.hasDelta) diffTables else noDiffMessage - - this.analysisFile.get().asFile.writeText(output) - } - - private fun writeLatestReleaseMetrics(file: File) = runBlocking { - S3Client.fromEnvironment().use { s3 -> - s3.getObject( - GetObjectRequest { - bucket = S3_ARTIFACT_SIZE_METRICS_BUCKET - key = "${pluginConfig.projectRepositoryName}-latest-release.csv" - }, - ) { latestReleaseMetrics -> - file.writeText( - latestReleaseMetrics.body?.decodeToString() ?: throw GradleException("Metrics from latest release are empty"), - ) - } - } - } - - private fun analyzeArtifactSizeMetrics( - releaseMetrics: Map, - currentMetrics: Map, - ): ArtifactSizeMetricsAnalysis { - val artifactNames = releaseMetrics.keys + currentMetrics.keys - val artifactSizeMetrics = artifactNames.associateWith { artifact -> - val current = currentMetrics[artifact] ?: 0 - val release = releaseMetrics[artifact] ?: 0 - - val delta = current - release - val percentage = if (release == 0L) Double.NaN else delta.toDouble() / release.toDouble() * 100 - - ArtifactSizeMetric( - current, - release, - delta, - percentage, - ) - } - - val changeHappened = artifactSizeMetrics.values.any { it.delta.isNotaFluctuation() } - val significantChange = artifactSizeMetrics.values.any { - // Increase in size above threshold or new artifact - (it.percentage > pluginConfig.significantChangeThresholdPercentage) || (it.latestReleaseSize == 0L) - } - - return ArtifactSizeMetricsAnalysis(artifactSizeMetrics, significantChange, changeHappened) - } - - // There are small fluctuations in artifact size that are not real delta - private fun Long.isNotaFluctuation() = abs(this) > 5L - - private data class ArtifactSizeMetricsAnalysis( - val metrics: Map, - val significantChange: Boolean, - val hasDelta: Boolean, - ) - - private fun createDiffTables(analysis: ArtifactSizeMetricsAnalysis): String = buildString { - appendLine("Affected Artifacts\n=") - - val requiresAttention = StringBuilder() - .appendLine("Significantly increased in size") - .appendLine() - .append(tableHeader) - var artifactRequiresAttention = false - - val ordinary = StringBuilder() - .appendLine("
") // See: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/organizing-information-with-collapsed-sections - .appendLine("Changed in size") - .appendLine() - .append(tableHeader) - var artifactOrdinaryChange = false - - analysis.metrics - .toList() - .sortedByDescending { it.second.percentage } - .toMap() - .forEach { metric -> - if (metric.value.delta.isNotaFluctuation()) { - val row = buildString { - append("|") - append(metric.key) - append("|") - if (metric.value.currentSize == 0L) append("(does not exist)") else append("%,d".format(metric.value.currentSize)) - append("|") - if (metric.value.latestReleaseSize == 0L) append("(does not exist)") else append("%,d".format(metric.value.latestReleaseSize)) - append("|") - append("%,d".format(metric.value.delta)) - append("|") - append("%.2f".format(metric.value.percentage)) - append("%") - appendLine("|") - } - - if (metric.value.requiresAttention()) { - requiresAttention.append(row) - artifactRequiresAttention = true - } else { - ordinary.append(row) - artifactOrdinaryChange = true - } - } - } - - ordinary - .appendLine() - .append("
") - - if (artifactRequiresAttention) appendLine(requiresAttention) - if (artifactOrdinaryChange) appendLine(ordinary) - } - - private fun ArtifactSizeMetric.requiresAttention() = this.percentage > pluginConfig.significantChangeThresholdPercentage || this.percentage.isNaN() - - private data class ArtifactSizeMetric( - val currentSize: Long, - val latestReleaseSize: Long, - val delta: Long, - val percentage: Double, - ) - - private fun File.toMap(): Map { - val metrics = this - .readLines() - .drop(1) // Ignoring header - .map { metricLine -> - metricLine.split(",").map { it.trim() } // e.g. ["S3-jvm.jar", "103948"] - } - - return metrics.associate { metric -> - metric[0] to metric[1].toLong() - } - } - - private val noDiffMessage = """ - Affected Artifacts - = - No artifacts changed size - """.trimIndent() - - private val tableHeader = buildString { - appendLine("| Artifact |Pull Request (bytes) | Latest Release (bytes) | Delta (bytes) | Delta (percentage) |") - appendLine("| -------- | ------------------: | ---------------------: | ------------: | -----------------: |") - } -} diff --git a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/ArtifactSizeMetricsPlugin.kt b/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/ArtifactSizeMetricsPlugin.kt deleted file mode 100644 index b4ea5bf..0000000 --- a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/ArtifactSizeMetricsPlugin.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package aws.sdk.kotlin.gradle.plugins.artifactsizemetrics - -import aws.sdk.kotlin.gradle.util.verifyRootProject -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.tasks.TaskProvider -import org.gradle.jvm.tasks.Jar -import org.gradle.kotlin.dsl.register -import org.gradle.kotlin.dsl.withType - -private const val TASK_GROUP = "Verification" -internal const val OUTPUT_PATH = "reports/metrics/" -internal const val S3_ARTIFACT_SIZE_METRICS_BUCKET = "artifact-size-metrics" // Owned by: aws-kotlin-sdk+ci - -/** - * Facilitates the collection and analysis of artifact size metrics via the `artifactSizeMetrics` and `analyzeArtifactSizeMetrics` gradle tasks. - * Includes additional tasks for CI to run. - */ -class ArtifactSizeMetricsPlugin : Plugin { - override fun apply(target: Project) { - target.verifyRootProject { "${this::class.java} can only be applied to the root project" } - - target.extensions.create("artifactSizeMetrics", ArtifactSizeMetricsPluginConfig::class.java) - - val tasks = mutableListOf>() - target.subprojects { tasks.add(subprojectArtifactSizeMetricsTask()) } - - target.registerRootProjectArtifactSizeMetricsTask(tasks) - - target.tasks.register("collectDelegatedArtifactSizeMetrics") { group = TASK_GROUP } - target.tasks.register("analyzeArtifactSizeMetrics") { group = TASK_GROUP } - target.tasks.register("putArtifactSizeMetricsInCloudWatch") { group = TASK_GROUP } - target.tasks.register("saveArtifactSizeMetrics") { group = TASK_GROUP } - } -} - -private fun Project.subprojectArtifactSizeMetricsTask(): TaskProvider = tasks.register("artifactSizeMetrics") { - group = TASK_GROUP - onlyIf { tasks.findByName("jvmJar") != null } - dependsOn(tasks.withType()) -} - -private fun Project.registerRootProjectArtifactSizeMetricsTask( - subProjects: List>, -) { - tasks.register("artifactSizeMetrics") { - group = TASK_GROUP - dependsOn(subProjects) - val artifactSizeMetricsFile = layout.buildDirectory.file(OUTPUT_PATH + "artifact-size-metrics.csv") - outputs.file(artifactSizeMetricsFile) - - doLast { - val subProjectArtifactSizeMetrics = mutableListOf() - - subProjects - .map { it.get().metricsFile.asFile.get() } - .filter { it.exists() && it.length() > 0 } - .forEach { metricsFile -> - metricsFile - .readLines() - .drop(1) // Remove header - .forEach { metric -> - subProjectArtifactSizeMetrics.add(metric) - } - } - - val projectArtifactSizeMetrics = buildString { - val header = "Artifact, Size" - appendLine(header) - - subProjectArtifactSizeMetrics.forEach { entry -> - appendLine(entry) - } - } - - artifactSizeMetricsFile.get().asFile.writeText(projectArtifactSizeMetrics) - } - } -} - -open class ArtifactSizeMetricsPluginConfig { - /** - * Changes the prefix used to get artifact size metrics in the - * "collectDelegatedArtifactSizeMetrics" task. - */ - var bucketPrefixOverride: String? = null - - /** - * The gradle project name prefixes to collect metrics on. Check projects using "./gradlew project" - */ - var artifactPrefixes: Set = emptySet() - - /** - * The gradle project name prefixes to collect metrics on. This will consider the whole closure. - * Check projects using "./gradlew project" - */ - var closurePrefixes: Set = emptySet() - - /** - * The threshold for an acceptable artifact size increase (percentage) - */ - var significantChangeThresholdPercentage: Double = 5.0 - - /** - * The GitHub repository name for the project - */ - var projectRepositoryName: String? = null - get() { - check(!field.isNullOrEmpty()) { - missingProjectRepositoryNameMessage - } - return field - } -} - -internal val missingProjectRepositoryNameMessage = """ - Please specify a repository name in the plugin DSL for this project. - In build.gradle.kts: - - artifactSizeMetrics { - ${ArtifactSizeMetricsPluginConfig::projectRepositoryName.name} = "YOUR_REPOSITORY_NAME" - } -""".trimIndent() diff --git a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/CollectArtifactSizeMetrics.kt b/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/CollectArtifactSizeMetrics.kt deleted file mode 100644 index 1e4a4a5..0000000 --- a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/CollectArtifactSizeMetrics.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package aws.sdk.kotlin.gradle.plugins.artifactsizemetrics - -import org.gradle.api.DefaultTask -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.jvm.tasks.Jar -import org.gradle.kotlin.dsl.getByName - -/** - * Gradle task that collects artifact size metrics for configured projects and closures. - * Outputs the results to a CSV file. - */ -internal abstract class CollectArtifactSizeMetrics : DefaultTask() { - /** - * The file where the artifact size metrics will be stored, defaults to /build/reports/metrics/artifact-size-metrics.csv - */ - @get:OutputFile - abstract val metricsFile: RegularFileProperty - - init { - metricsFile.convention(project.layout.buildDirectory.file(OUTPUT_PATH + "artifact-size-metrics.csv")) - } - - @TaskAction - fun collect() { - val pluginConfig = this.project.rootProject.extensions.getByType(ArtifactSizeMetricsPluginConfig::class.java) - if (pluginConfig.artifactPrefixes.none { project.path.startsWith(it) }) return - - // TODO: Start collecting metrics for KMP artifacts - val jvmJarTask = project.tasks.getByName("jvmJar") - val jarSize = jvmJarTask.archiveFile.get().asFile.length() - val artifact = buildString { - append(jvmJarTask.archiveBaseName.get()) - append("-") - append(jvmJarTask.archiveAppendix.orNull ?: "unknown") - append(".") - append(jvmJarTask.archiveExtension.get()) - } - - var closureSize: Long? = null - if (pluginConfig.closurePrefixes.any { project.path.startsWith(it) }) { - closureSize = jarSize + project.configurations.getByName("jvmRuntimeClasspath").sumOf { it.length() } - } - - val metrics = buildString { - val header = "Artifact, Size" - appendLine(header) - - append(artifact) - append(",") - appendLine(jarSize) - - if (closureSize != null) { - append("$artifact closure") - append(",") - appendLine(closureSize) - } - } - - metricsFile.asFile.get().writeText(metrics) - } -} diff --git a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/CollectDelegatedArtifactSizeMetrics.kt b/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/CollectDelegatedArtifactSizeMetrics.kt deleted file mode 100644 index ae8971d..0000000 --- a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/CollectDelegatedArtifactSizeMetrics.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package aws.sdk.kotlin.gradle.plugins.artifactsizemetrics - -import aws.sdk.kotlin.gradle.util.AwsSdkGradleException -import aws.sdk.kotlin.services.s3.S3Client -import aws.sdk.kotlin.services.s3.listObjects -import aws.sdk.kotlin.services.s3.model.GetObjectRequest -import aws.smithy.kotlin.runtime.content.decodeToString -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.runBlocking -import org.gradle.api.DefaultTask -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction - -/** - * Collects the delegated artifact size metrics for a projects subprojects from S3, combines them and outputs them to a file. - * This task should typically be run after codebuild gathers metrics and puts them in S3 during CI but can also be used to - * query the metrics bucket if you modify the file key filter. - */ -internal abstract class CollectDelegatedArtifactSizeMetrics : DefaultTask() { - /** - * The file where the artifact size metrics will be stored, defaults to build/reports/metrics/artifact-size-metrics.csv - */ - @get:OutputFile - abstract val metricsFile: RegularFileProperty - - init { - metricsFile.convention(project.layout.buildDirectory.file(OUTPUT_PATH + "artifact-size-metrics.csv")) - } - - private val pluginConfig = project.rootProject.extensions.getByType(ArtifactSizeMetricsPluginConfig::class.java) - - @TaskAction - fun collect() { - val pullRequestNumber = project.findProperty("pullRequest")?.toString()?.takeIf { it.isNotEmpty() } - val releaseTag = project.findProperty("release")?.toString()?.takeIf { it.isNotEmpty() } - val identifier = pullRequestNumber ?: releaseTag ?: throw AwsSdkGradleException("Please specify a pull request or release number") - - val artifactSizeMetricsFileKeys = getFileKeys(identifier) ?: throw AwsSdkGradleException("Unable to list objects from artifact size metrics bucket") - val artifactSizeMetricsFiles = getFiles(artifactSizeMetricsFileKeys) - val combined = combine(artifactSizeMetricsFiles) - - metricsFile.asFile.get().writeText(combined) - } - - private fun getFileKeys(identifier: String): List? = runBlocking { - val prefixes = pluginConfig.bucketPrefixOverride?.let { listOf(it) } ?: listOf( - "[TEMP]${pluginConfig.projectRepositoryName}-$identifier-", - "[TEMP]private-${pluginConfig.projectRepositoryName}-staging-$identifier-", // private repo metrics files have different prefix - ) - - return@runBlocking prefixes.firstNotNullOfOrNull { prefix -> - S3Client.fromEnvironment().use { s3 -> - s3.listObjects { - bucket = S3_ARTIFACT_SIZE_METRICS_BUCKET - this.prefix = prefix - }.contents?.map { - it.key ?: throw AwsSdkGradleException("A file from the artifact size metrics bucket is missing a key") - } - } - } - } - - private fun getFiles(keys: List): List = runBlocking { - S3Client.fromEnvironment().use { s3 -> - keys.map { key -> - async { - s3.getObject( - GetObjectRequest { - bucket = S3_ARTIFACT_SIZE_METRICS_BUCKET - this.key = key - }, - ) { it.body?.decodeToString() ?: throw AwsSdkGradleException("Metrics file $key is missing a body") } - } - }.awaitAll() - } - } - - private fun combine(metricsFiles: List) = buildString { - appendLine("Artifact, Size") - metricsFiles.forEach { metricsFile -> - metricsFile - .split("\n") - .drop(1) // Remove header - .forEach { metric -> - if (metric.isNotEmpty()) { - appendLine(metric) - } - } - } - } -} diff --git a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/PutArtifactSizeMetricsInCloudWatch.kt b/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/PutArtifactSizeMetricsInCloudWatch.kt deleted file mode 100644 index 73ec062..0000000 --- a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/PutArtifactSizeMetricsInCloudWatch.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package aws.sdk.kotlin.gradle.plugins.artifactsizemetrics - -import aws.sdk.kotlin.services.cloudwatch.CloudWatchClient -import aws.sdk.kotlin.services.cloudwatch.model.Dimension -import aws.sdk.kotlin.services.cloudwatch.model.MetricDatum -import aws.sdk.kotlin.services.cloudwatch.model.StandardUnit -import aws.sdk.kotlin.services.cloudwatch.putMetricData -import aws.smithy.kotlin.runtime.time.Instant -import kotlinx.coroutines.runBlocking -import org.gradle.api.DefaultTask -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.TaskAction - -/** - * Puts a projects local artifact size metrics in CloudWatch. - * This task should typically be run after gathering metrics for a release in our CI - */ -internal abstract class PutArtifactSizeMetricsInCloudWatch : DefaultTask() { - /** - * File containing the project's current computed artifact size metrics. - */ - @get:InputFile - abstract val metricsFile: RegularFileProperty - - init { - metricsFile.convention(project.layout.buildDirectory.file(OUTPUT_PATH + "artifact-size-metrics.csv")) - } - - @TaskAction - fun put() { - val currentTime = Instant.now() - val pluginConfig = project.rootProject.extensions.getByType(ArtifactSizeMetricsPluginConfig::class.java) - - val metrics = metricsFile - .get() - .asFile - .readLines() - .drop(1) // Ignoring header - .map { metric -> - val split = metric.split(",").map { it.trim() } - val artifactName = split[0] - val artifactSize = split[1].toDouble() // CloudWatch's requires metric values to be double - - MetricDatum { - metricName = "${pluginConfig.projectRepositoryName}-$artifactName" - timestamp = currentTime - unit = StandardUnit.Bytes - value = artifactSize - dimensions = listOf( - Dimension { - name = "Project" - value = pluginConfig.projectRepositoryName - }, - ) - } - } - - runBlocking { - CloudWatchClient.fromEnvironment().use { cloudWatch -> - metrics - .chunked(1000) // CloudWatch allows up to 1000 metrics at a time - .forEach { chunk -> - cloudWatch.putMetricData { - namespace = "Artifact Size Metrics" - metricData = chunk - } - } - } - } - } -} diff --git a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/SaveArtifactSizeMetrics.kt b/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/SaveArtifactSizeMetrics.kt deleted file mode 100644 index 299fb8c..0000000 --- a/build-plugins/build-support/src/main/kotlin/aws/sdk/kotlin/gradle/plugins/artifactsizemetrics/SaveArtifactSizeMetrics.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package aws.sdk.kotlin.gradle.plugins.artifactsizemetrics - -import aws.sdk.kotlin.gradle.util.stringPropertyNotNull -import aws.sdk.kotlin.services.s3.S3Client -import aws.sdk.kotlin.services.s3.putObject -import aws.smithy.kotlin.runtime.content.ByteStream -import kotlinx.coroutines.runBlocking -import org.gradle.api.DefaultTask -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.TaskAction - -/** - * Puts artifact size metrics in S3 to save them. - * We put them in CloudWatch also (as metrics) but CloudWatch only keeps metrics temporarily. - */ -internal abstract class SaveArtifactSizeMetrics : DefaultTask() { - /** - * File containing the project's artifact size metrics. - */ - @get:InputFile - abstract val metricsFile: RegularFileProperty - - init { - metricsFile.convention(project.layout.buildDirectory.file(OUTPUT_PATH + "artifact-size-metrics.csv")) - } - - private val pluginConfig = project.rootProject.extensions.getByType(ArtifactSizeMetricsPluginConfig::class.java) - - @TaskAction - fun save() { - val releaseTag = project.stringPropertyNotNull("release") - val artifactSizeMetrics = ByteStream.fromString(metricsFile.get().asFile.readText()) - - runBlocking { - S3Client.fromEnvironment().use { s3 -> - s3.putObject { - bucket = S3_ARTIFACT_SIZE_METRICS_BUCKET - key = "${pluginConfig.projectRepositoryName}-latest-release.csv" - body = artifactSizeMetrics - } - - s3.putObject { - bucket = S3_ARTIFACT_SIZE_METRICS_BUCKET - key = "${pluginConfig.projectRepositoryName}-$releaseTag-release.csv" - body = artifactSizeMetrics - } - } - } - } -} From 033d6e20921bc7aa8d21848cfaa58ecef0451262 Mon Sep 17 00:00:00 2001 From: 0marperez <60363173+0marperez@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:05:37 -0400 Subject: [PATCH 16/20] remove description from action step --- .../artifact-size-metrics/download-and-process/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/artifact-size-metrics/download-and-process/action.yml b/.github/actions/artifact-size-metrics/download-and-process/action.yml index 3de5b9a..c2a425d 100644 --- a/.github/actions/artifact-size-metrics/download-and-process/action.yml +++ b/.github/actions/artifact-size-metrics/download-and-process/action.yml @@ -9,8 +9,8 @@ inputs: runs: using: composite steps: + # Compares artifact size metrics and sets LARGE_DIFF to true if a size increase of ≥5% is detected. - name: Download and process artifact size metrics - description: Compares artifact size metrics and sets LARGE_DIFF to true if a size increase of ≥5% is detected. shell: bash env: DOWNLOAD: ${{ inputs.download }} From 3aca1415d51a762755ae7956af1a553cfb95e318 Mon Sep 17 00:00:00 2001 From: 0marperez <60363173+0marperez@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:10:56 -0400 Subject: [PATCH 17/20] remove asm comment --- .../download-and-process/action.yml | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/.github/actions/artifact-size-metrics/download-and-process/action.yml b/.github/actions/artifact-size-metrics/download-and-process/action.yml index c2a425d..2fde357 100644 --- a/.github/actions/artifact-size-metrics/download-and-process/action.yml +++ b/.github/actions/artifact-size-metrics/download-and-process/action.yml @@ -20,42 +20,6 @@ runs: chmod +x ../utils/download-and-process/main.sh ../utils/download-and-process/main.sh - - name: Comment artifact size metrics comparison - if: github.event_name == 'pull_request' - uses: actions/github-script@v7 - with: - script: | - const fs = require('node:fs') - - // Read the artifact size metrics comparison - const comparison = fs.readFileSync('build/reports/metrics/artifact-analysis.md', 'utf8') - - // Get PR ID - const { data: pullRequest } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number - }) - const pullRequestId = pullRequest.node_id - - // Add the comment - const addCommentResponse = await github.graphql(` - mutation { - addComment(input: {body: """${comparison}""", subjectId: "${pullRequestId}"}) { - commentEdge { node { id } } - } - } - `) - - const commentId = addCommentResponse.addComment.commentEdge.node.id - - // Minimize the comment - await github.graphql(` - mutation { - minimizeComment(input: {subjectId: "${commentId}", classifier: RESOLVED}) { } - } - `) - - name: Large size increase? if: ${{ !contains(github.event.pull_request.labels.*.name, 'acknowledge-artifact-size-increase') }} shell: bash From 2c6dc79f3f3447f25d2951a1c4d359a1bd941458 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Fri, 17 Oct 2025 15:13:46 -0400 Subject: [PATCH 18/20] setup download and process for debugging --- .../artifact-size-metrics/download-and-process/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/artifact-size-metrics/download-and-process/action.yml b/.github/actions/artifact-size-metrics/download-and-process/action.yml index 2fde357..84043d4 100644 --- a/.github/actions/artifact-size-metrics/download-and-process/action.yml +++ b/.github/actions/artifact-size-metrics/download-and-process/action.yml @@ -17,6 +17,8 @@ runs: GITHUB_REPOSITORY: ${{ github.repository }} IDENTIFIER: ${{ github.ref_name }} run: | + pwd + ls ../ chmod +x ../utils/download-and-process/main.sh ../utils/download-and-process/main.sh From 55eb90bc24cbe86011127bf09750f8df958ba7bc Mon Sep 17 00:00:00 2001 From: 0marperez Date: Fri, 17 Oct 2025 15:22:44 -0400 Subject: [PATCH 19/20] fix actions script path --- .../artifact-size-metrics/calculate-and-upload/action.yml | 4 ++-- .../artifact-size-metrics/download-and-process/action.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/artifact-size-metrics/calculate-and-upload/action.yml b/.github/actions/artifact-size-metrics/calculate-and-upload/action.yml index 0761ab7..c64e68c 100644 --- a/.github/actions/artifact-size-metrics/calculate-and-upload/action.yml +++ b/.github/actions/artifact-size-metrics/calculate-and-upload/action.yml @@ -20,5 +20,5 @@ runs: UPLOAD: ${{ inputs.upload }} RELEASE_METRICS: ${{ inputs.release_metrics }} run: | - chmod +x ../utils/calculate-and-upload/main.sh - ../utils/calculate-and-upload/main.sh + chmod +x $GITHUB_ACTION_PATH/../utils/calculate-and-upload/main.sh + $GITHUB_ACTION_PATH/../utils/calculate-and-upload/main.sh diff --git a/.github/actions/artifact-size-metrics/download-and-process/action.yml b/.github/actions/artifact-size-metrics/download-and-process/action.yml index 84043d4..ce92268 100644 --- a/.github/actions/artifact-size-metrics/download-and-process/action.yml +++ b/.github/actions/artifact-size-metrics/download-and-process/action.yml @@ -19,8 +19,8 @@ runs: run: | pwd ls ../ - chmod +x ../utils/download-and-process/main.sh - ../utils/download-and-process/main.sh + chmod +x $GITHUB_ACTION_PATH/../utils/download-and-process/main.sh + $GITHUB_ACTION_PATH/../utils/download-and-process/main.sh - name: Large size increase? if: ${{ !contains(github.event.pull_request.labels.*.name, 'acknowledge-artifact-size-increase') }} From bdab52664660cf3c4055627bd35ba3e8433bf2d4 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Fri, 17 Oct 2025 15:29:35 -0400 Subject: [PATCH 20/20] remove debug commmands --- .../artifact-size-metrics/download-and-process/action.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/actions/artifact-size-metrics/download-and-process/action.yml b/.github/actions/artifact-size-metrics/download-and-process/action.yml index ce92268..17ae5c6 100644 --- a/.github/actions/artifact-size-metrics/download-and-process/action.yml +++ b/.github/actions/artifact-size-metrics/download-and-process/action.yml @@ -17,8 +17,6 @@ runs: GITHUB_REPOSITORY: ${{ github.repository }} IDENTIFIER: ${{ github.ref_name }} run: | - pwd - ls ../ chmod +x $GITHUB_ACTION_PATH/../utils/download-and-process/main.sh $GITHUB_ACTION_PATH/../utils/download-and-process/main.sh