From dc4ef47e850b16261c4eed6b8b742813399dcc71 Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 2 Aug 2024 18:50:40 +0200 Subject: [PATCH 01/17] Initial script with improvements which shows full `xcbeautify` log and error when error encountered --- Scripts/propose_next_version.sh | 152 ++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 Scripts/propose_next_version.sh diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh new file mode 100644 index 0000000..c1fc44a --- /dev/null +++ b/Scripts/propose_next_version.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail +trap 'error_handler ${FUNCNAME-main context} ${LINENO} $?' ERR + +# CONSTANTS + +readonly CALL_DIR="$PWD" +readonly SCRIPT_NAME=$(basename -s ".sh" "$0") +readonly TEMP_DIRECTORY="tmp$RANDOM" + +# FUNCTIONS + +function error_handler() { + echo "$SCRIPT_NAME.sh: in '$1()', line $2: error: $3" + exit 1 +} + +# Scans git history, starting from active branch HEAD, to find latest pushed tag version. +# Version has to be represented in plain semver format, e.g. '1.0.1'. +# Modify regex for different version patter scan. +function get_current_version_tag_name() { + local current_branch_name + local last_reference_tag_name + + current_branch_name=$(git rev-parse --abbrev-ref HEAD) + last_reference_tag_name=$(git tag --merged="$current_branch_name" --list --sort=-version:refname "[0-9]*.[0-9]*.[0-9]*" | head -n 1) + cat <<< "$last_reference_tag_name" +} + +function build_public_interface() { + xcodebuild \ + -archivePath "$ARCHIVE_PATH" \ + -derivedDataPath "$DERIVED_DATA_PATH" \ + -destination "$DESTINATION" \ + -scheme "$SCHEME" \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + SKIP_INSTALL=NO \ + OTHER_SWIFT_FLAGS="-no-verify-emitted-module-interface" \ + archive | xcbeautify +} + +function make_public_interface() { + local package_name + + package_name=$(swift_package_name) + + if ! { error=$(build_public_interface 2>&1); }; then + echo "$error" + exit 1 + fi + + MAKE_PUBLIC_INTERFACE=$(find "./$DERIVED_DATA_PATH/" -name "${package_name}.swiftinterface") +} + +function swift_package_name() { + swift package describe | awk '/Name:/ { print $2; exit; }' +} + +function main() { + local current_branch_name + local current_public_interface_path + local has_breaking_changes + local has_additive_changes + local temp_version_directory + local version_public_interface_path + local version_tag + + local -r semantic_version_regex='([0-9]+).([0-9]+).([0-9]+)' + + current_branch_name=$(git rev-parse --abbrev-ref HEAD) + version_tag=$(get_current_version_tag_name) + temp_version_directory="$TEMP_DIRECTORY/version" + + # Clean up derived data directory to prevent of any cached files usage. + rm -rf "$DERIVED_DATA_PATH" + + # Copy change tagged with given version tag to 'tmp{random}/version' directory by using clone of local repo and checkouting to version tag. + git clone "$CALL_DIR" "$temp_version_directory" --quiet + cd "$temp_version_directory" + + # git checkout "tags/$version_tag" --force --quiet --recurse-submodules + + # Get public interface from the change marked with version tag. + make_public_interface + version_public_interface_path="$MAKE_PUBLIC_INTERFACE" + + # Go back to the project root. + cd "$CALL_DIR" + + # Get public interface from the current change. + make_public_interface + current_public_interface_path="$MAKE_PUBLIC_INTERFACE" + + # Make public interfaces diffs + has_breaking_changes=$(diff "$version_public_interface_path" "$current_public_interface_path" | grep -c -i "^<" || true) + has_additive_changes=$(diff "$version_public_interface_path" "$current_public_interface_path" | grep -c -i "^>" || true) + + # Create version based on diff output + if [[ ! $version_tag =~ $semantic_version_regex ]]; then + cat <<< "$version_tag" + else + local major="${BASH_REMATCH[1]}" + local minor="${BASH_REMATCH[2]}" + local patch="${BASH_REMATCH[3]}" + + if [[ $has_breaking_changes -gt 0 ]]; then + major=$((major+1)) + minor=0 + patch=0 + elif [[ $has_additive_changes -gt 0 ]]; then + minor=$((minor+1)) + patch=0 + else + patch=$((patch+1)) + fi + + cat <<< "${major}.${minor}.${patch}" + fi + + rm -rf "$TEMP_DIRECTORY" +} + +# ENTRY POINT + +while [[ $# -gt 0 ]]; do + case $1 in + # Device for which public interface is created. Use value supported by `-destination` argument in `xcodebuild archive`. + # E.g. `platform=iOS Simulator,name=iPhone 14,OS=17.0`. + -d|--device) + DESTINATION=${2} + shift 2 + ;; + # Derived data path. + -r|--derived-data-path) + DERIVED_DATA_PATH=${2} + ARCHIVE_PATH="$DERIVED_DATA_PATH/archive" + shift 2 + ;; + # Package scheme name. For packages with multiple targets, it may be required to add `-Package` suffix. + -s|--scheme) + SCHEME=${2} + shift 2 + ;; + *) + echo "Unknown parameter: '${1}'. Please use supported parameters: '-d|--device', '-r|--derived-data-path', '-s|--scheme'." + exit 1 + ;; + esac +done + +main From 05b533ebeff422abeee9bdcebcbda41993d4696f Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Thu, 15 Aug 2024 22:06:20 +0200 Subject: [PATCH 02/17] Add support for multiple package products --- Scripts/propose_next_version.sh | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh index c1fc44a..c8a908a 100644 --- a/Scripts/propose_next_version.sh +++ b/Scripts/propose_next_version.sh @@ -41,22 +41,35 @@ function build_public_interface() { } function make_public_interface() { - local package_name - - package_name=$(swift_package_name) + local package_products + local packages_public_interface_path if ! { error=$(build_public_interface 2>&1); }; then echo "$error" exit 1 fi - MAKE_PUBLIC_INTERFACE=$(find "./$DERIVED_DATA_PATH/" -name "${package_name}.swiftinterface") + packages_public_interface_path="./$DERIVED_DATA_PATH/public_interface.swiftinterface" + rm -rf "$packages_public_interface_path" + + package_products=$(swift_package_products) + + for package_product in $package_products + do + cat "$(find "./$DERIVED_DATA_PATH/" -name "${package_product}.swiftinterface")" >> "$packages_public_interface_path" + done + + MAKE_PUBLIC_INTERFACE="$packages_public_interface_path" } function swift_package_name() { swift package describe | awk '/Name:/ { print $2; exit; }' } +function swift_package_products() { + swift package describe --type json | jq -r '.products.[].name' +} + function main() { local current_branch_name local current_public_interface_path From be5d723cbb9869e3585814c765c8cb82bc1c9178 Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Feb 2025 17:09:12 +0100 Subject: [PATCH 03/17] Remove unused method --- Scripts/propose_next_version.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh index c8a908a..cc1005f 100644 --- a/Scripts/propose_next_version.sh +++ b/Scripts/propose_next_version.sh @@ -62,10 +62,6 @@ function make_public_interface() { MAKE_PUBLIC_INTERFACE="$packages_public_interface_path" } -function swift_package_name() { - swift package describe | awk '/Name:/ { print $2; exit; }' -} - function swift_package_products() { swift package describe --type json | jq -r '.products.[].name' } From 65578a164bad0bb85af004567f6fd9182bee5a4b Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Feb 2025 17:09:28 +0100 Subject: [PATCH 04/17] Enhance comments --- Scripts/propose_next_version.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh index cc1005f..0829e3c 100644 --- a/Scripts/propose_next_version.sh +++ b/Scripts/propose_next_version.sh @@ -140,13 +140,15 @@ while [[ $# -gt 0 ]]; do DESTINATION=${2} shift 2 ;; - # Derived data path. + # Derived data path (optional). -r|--derived-data-path) DERIVED_DATA_PATH=${2} ARCHIVE_PATH="$DERIVED_DATA_PATH/archive" shift 2 ;; # Package scheme name. For packages with multiple targets, it may be required to add `-Package` suffix. + # Example: your package is named `ClientService` and has two targets inside: `ClientServiceDTOs` and `ClientServiceAPI`. + # Then, your target would be `ClientService-Package`. -s|--scheme) SCHEME=${2} shift 2 From bb4c6cfe70f4614b744d6ea8b40dd71234485770 Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Feb 2025 17:09:57 +0100 Subject: [PATCH 05/17] Change copy folder --- Scripts/propose_next_version.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh index 0829e3c..019dd85 100644 --- a/Scripts/propose_next_version.sh +++ b/Scripts/propose_next_version.sh @@ -46,6 +46,7 @@ function make_public_interface() { if ! { error=$(build_public_interface 2>&1); }; then echo "$error" + echo "Cannot complete the build due to the compile error. Check logs above." exit 1 fi @@ -79,16 +80,15 @@ function main() { current_branch_name=$(git rev-parse --abbrev-ref HEAD) version_tag=$(get_current_version_tag_name) - temp_version_directory="$TEMP_DIRECTORY/version" + temp_version_directory="$TEMP_DIRECTORY" # Clean up derived data directory to prevent of any cached files usage. rm -rf "$DERIVED_DATA_PATH" - # Copy change tagged with given version tag to 'tmp{random}/version' directory by using clone of local repo and checkouting to version tag. + # Copy change tagged with given version tag to 'tmp{random}' directory by using clone of local repo and checkouting to version tag. git clone "$CALL_DIR" "$temp_version_directory" --quiet cd "$temp_version_directory" - - # git checkout "tags/$version_tag" --force --quiet --recurse-submodules + git checkout "tags/$version_tag" --force --quiet --recurse-submodules # Get public interface from the change marked with version tag. make_public_interface From 3b15efb291651e9101f2ed9efb6583dabbfbe9b0 Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Feb 2025 17:10:22 +0100 Subject: [PATCH 06/17] Add temp folder cleanup --- Scripts/propose_next_version.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh index 019dd85..e6faafd 100644 --- a/Scripts/propose_next_version.sh +++ b/Scripts/propose_next_version.sh @@ -13,6 +13,7 @@ readonly TEMP_DIRECTORY="tmp$RANDOM" function error_handler() { echo "$SCRIPT_NAME.sh: in '$1()', line $2: error: $3" + reset exit 1 } @@ -127,7 +128,12 @@ function main() { cat <<< "${major}.${minor}.${patch}" fi - rm -rf "$TEMP_DIRECTORY" + reset +} + +function reset() { + cd "$CALL_DIR" + rm -rf "$TEMP_DIRECTORY" > /dev/null } # ENTRY POINT From 057fea85e21776ea890e7e717d9a9b6e58a9ec06 Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Feb 2025 19:07:06 +0100 Subject: [PATCH 07/17] Tweak file path for diffing --- Scripts/propose_next_version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh index e6faafd..ba4e338 100644 --- a/Scripts/propose_next_version.sh +++ b/Scripts/propose_next_version.sh @@ -93,7 +93,7 @@ function main() { # Get public interface from the change marked with version tag. make_public_interface - version_public_interface_path="$MAKE_PUBLIC_INTERFACE" + version_public_interface_path="$temp_version_directory/$MAKE_PUBLIC_INTERFACE" # Go back to the project root. cd "$CALL_DIR" From 3c0524c1dae961aabb1c4a623c10b33d51e305a0 Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Feb 2025 19:07:21 +0100 Subject: [PATCH 08/17] Add manifests diffing --- Scripts/propose_next_version.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh index ba4e338..683623e 100644 --- a/Scripts/propose_next_version.sh +++ b/Scripts/propose_next_version.sh @@ -6,6 +6,7 @@ trap 'error_handler ${FUNCNAME-main context} ${LINENO} $?' ERR # CONSTANTS readonly CALL_DIR="$PWD" +readonly MANIFEST_NAME="Package.swift" readonly SCRIPT_NAME=$(basename -s ".sh" "$0") readonly TEMP_DIRECTORY="tmp$RANDOM" @@ -70,10 +71,12 @@ function swift_package_products() { function main() { local current_branch_name + local current_manifest_path local current_public_interface_path local has_breaking_changes local has_additive_changes local temp_version_directory + local version_manifest_path local version_public_interface_path local version_tag @@ -94,6 +97,7 @@ function main() { # Get public interface from the change marked with version tag. make_public_interface version_public_interface_path="$temp_version_directory/$MAKE_PUBLIC_INTERFACE" + version_manifest_path="$temp_version_directory/$MANIFEST_NAME" # Go back to the project root. cd "$CALL_DIR" @@ -101,9 +105,15 @@ function main() { # Get public interface from the current change. make_public_interface current_public_interface_path="$MAKE_PUBLIC_INTERFACE" + current_manifest_path="$MANIFEST_NAME" + + # Check manifest diff. Any change applied to manifest may introduce a breaking change. + # Example: adding a new dependendcy with specific version may break somebody's code they use same dependendcy but with different version. + # Swift Package Manager does not allow to use same dependendcy with two or more different versions. + has_manifest_changes=$(diff "$version_manifest_path" "$current_manifest_path" | grep -c -i "^<" || true) # Make public interfaces diffs - has_breaking_changes=$(diff "$version_public_interface_path" "$current_public_interface_path" | grep -c -i "^<" || true) + has_breaking_changes=$(diff "$version_public_interface_path" "$current_public_interface_path" | grep -c -i "^<" || true) || $has_manifest_changes has_additive_changes=$(diff "$version_public_interface_path" "$current_public_interface_path" | grep -c -i "^>" || true) # Create version based on diff output From 175e0a324c28081525015df590a2c20080a7fcd6 Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Tue, 25 Feb 2025 22:25:11 +0100 Subject: [PATCH 09/17] Improve clone performance --- Scripts/propose_next_version.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh index 683623e..a115348 100644 --- a/Scripts/propose_next_version.sh +++ b/Scripts/propose_next_version.sh @@ -90,9 +90,8 @@ function main() { rm -rf "$DERIVED_DATA_PATH" # Copy change tagged with given version tag to 'tmp{random}' directory by using clone of local repo and checkouting to version tag. - git clone "$CALL_DIR" "$temp_version_directory" --quiet + git clone "$CALL_DIR" --branch "$version_tag" --single-branch "$temp_version_directory" --quiet --recurse-submodules -c advice.detachedHead=false cd "$temp_version_directory" - git checkout "tags/$version_tag" --force --quiet --recurse-submodules # Get public interface from the change marked with version tag. make_public_interface From 8d58b3b4f69e958e3f08a51ec4efc96c694dec0d Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Sun, 23 Feb 2025 17:44:44 +0100 Subject: [PATCH 10/17] WIP versioning action --- .github/workflows/action-test.yml | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/action-test.yml diff --git a/.github/workflows/action-test.yml b/.github/workflows/action-test.yml new file mode 100644 index 0000000..f793bb5 --- /dev/null +++ b/.github/workflows/action-test.yml @@ -0,0 +1,42 @@ +name: Custom github action test + +on: + push: + branches: [] + +env: + DEVELOPER_DIR: "/Applications/Xcode_16.0.app/Contents/Developer" + +jobs: + test-custom-action: + name: Test custom action + runs-on: macos-15 + outputs: + next-version: ${{ steps.test-action.outputs.proposed-next-version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + + - name: Test custom action + id: test-action + uses: Filozoff/action-swift-propose-next-version@v1 + with: + device: "platform=iOS Simulator,name=iPhone 16,OS=18.0" + scheme: "XCTestExtension" + + push-tag: + name: Create and push tag + needs: test-custom-action + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Push tag + uses: Filozoff/action-make-tag@v1 + with: + commit-sha: "${{ github.sha }}" + tag: ${{ needs.test-custom-action.outputs.next-version }} + token: ${{ secrets.TAG_TOKEN }} + From 7abfcb2d0c09dad1b0f7846d93efa0f4d00626dd Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Mar 2025 12:19:15 +0100 Subject: [PATCH 11/17] Remove unnecessary file --- Scripts/propose_next_version.sh | 178 -------------------------------- 1 file changed, 178 deletions(-) delete mode 100644 Scripts/propose_next_version.sh diff --git a/Scripts/propose_next_version.sh b/Scripts/propose_next_version.sh deleted file mode 100644 index a115348..0000000 --- a/Scripts/propose_next_version.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env bash - -set -Eeuo pipefail -trap 'error_handler ${FUNCNAME-main context} ${LINENO} $?' ERR - -# CONSTANTS - -readonly CALL_DIR="$PWD" -readonly MANIFEST_NAME="Package.swift" -readonly SCRIPT_NAME=$(basename -s ".sh" "$0") -readonly TEMP_DIRECTORY="tmp$RANDOM" - -# FUNCTIONS - -function error_handler() { - echo "$SCRIPT_NAME.sh: in '$1()', line $2: error: $3" - reset - exit 1 -} - -# Scans git history, starting from active branch HEAD, to find latest pushed tag version. -# Version has to be represented in plain semver format, e.g. '1.0.1'. -# Modify regex for different version patter scan. -function get_current_version_tag_name() { - local current_branch_name - local last_reference_tag_name - - current_branch_name=$(git rev-parse --abbrev-ref HEAD) - last_reference_tag_name=$(git tag --merged="$current_branch_name" --list --sort=-version:refname "[0-9]*.[0-9]*.[0-9]*" | head -n 1) - cat <<< "$last_reference_tag_name" -} - -function build_public_interface() { - xcodebuild \ - -archivePath "$ARCHIVE_PATH" \ - -derivedDataPath "$DERIVED_DATA_PATH" \ - -destination "$DESTINATION" \ - -scheme "$SCHEME" \ - BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ - SKIP_INSTALL=NO \ - OTHER_SWIFT_FLAGS="-no-verify-emitted-module-interface" \ - archive | xcbeautify -} - -function make_public_interface() { - local package_products - local packages_public_interface_path - - if ! { error=$(build_public_interface 2>&1); }; then - echo "$error" - echo "Cannot complete the build due to the compile error. Check logs above." - exit 1 - fi - - packages_public_interface_path="./$DERIVED_DATA_PATH/public_interface.swiftinterface" - rm -rf "$packages_public_interface_path" - - package_products=$(swift_package_products) - - for package_product in $package_products - do - cat "$(find "./$DERIVED_DATA_PATH/" -name "${package_product}.swiftinterface")" >> "$packages_public_interface_path" - done - - MAKE_PUBLIC_INTERFACE="$packages_public_interface_path" -} - -function swift_package_products() { - swift package describe --type json | jq -r '.products.[].name' -} - -function main() { - local current_branch_name - local current_manifest_path - local current_public_interface_path - local has_breaking_changes - local has_additive_changes - local temp_version_directory - local version_manifest_path - local version_public_interface_path - local version_tag - - local -r semantic_version_regex='([0-9]+).([0-9]+).([0-9]+)' - - current_branch_name=$(git rev-parse --abbrev-ref HEAD) - version_tag=$(get_current_version_tag_name) - temp_version_directory="$TEMP_DIRECTORY" - - # Clean up derived data directory to prevent of any cached files usage. - rm -rf "$DERIVED_DATA_PATH" - - # Copy change tagged with given version tag to 'tmp{random}' directory by using clone of local repo and checkouting to version tag. - git clone "$CALL_DIR" --branch "$version_tag" --single-branch "$temp_version_directory" --quiet --recurse-submodules -c advice.detachedHead=false - cd "$temp_version_directory" - - # Get public interface from the change marked with version tag. - make_public_interface - version_public_interface_path="$temp_version_directory/$MAKE_PUBLIC_INTERFACE" - version_manifest_path="$temp_version_directory/$MANIFEST_NAME" - - # Go back to the project root. - cd "$CALL_DIR" - - # Get public interface from the current change. - make_public_interface - current_public_interface_path="$MAKE_PUBLIC_INTERFACE" - current_manifest_path="$MANIFEST_NAME" - - # Check manifest diff. Any change applied to manifest may introduce a breaking change. - # Example: adding a new dependendcy with specific version may break somebody's code they use same dependendcy but with different version. - # Swift Package Manager does not allow to use same dependendcy with two or more different versions. - has_manifest_changes=$(diff "$version_manifest_path" "$current_manifest_path" | grep -c -i "^<" || true) - - # Make public interfaces diffs - has_breaking_changes=$(diff "$version_public_interface_path" "$current_public_interface_path" | grep -c -i "^<" || true) || $has_manifest_changes - has_additive_changes=$(diff "$version_public_interface_path" "$current_public_interface_path" | grep -c -i "^>" || true) - - # Create version based on diff output - if [[ ! $version_tag =~ $semantic_version_regex ]]; then - cat <<< "$version_tag" - else - local major="${BASH_REMATCH[1]}" - local minor="${BASH_REMATCH[2]}" - local patch="${BASH_REMATCH[3]}" - - if [[ $has_breaking_changes -gt 0 ]]; then - major=$((major+1)) - minor=0 - patch=0 - elif [[ $has_additive_changes -gt 0 ]]; then - minor=$((minor+1)) - patch=0 - else - patch=$((patch+1)) - fi - - cat <<< "${major}.${minor}.${patch}" - fi - - reset -} - -function reset() { - cd "$CALL_DIR" - rm -rf "$TEMP_DIRECTORY" > /dev/null -} - -# ENTRY POINT - -while [[ $# -gt 0 ]]; do - case $1 in - # Device for which public interface is created. Use value supported by `-destination` argument in `xcodebuild archive`. - # E.g. `platform=iOS Simulator,name=iPhone 14,OS=17.0`. - -d|--device) - DESTINATION=${2} - shift 2 - ;; - # Derived data path (optional). - -r|--derived-data-path) - DERIVED_DATA_PATH=${2} - ARCHIVE_PATH="$DERIVED_DATA_PATH/archive" - shift 2 - ;; - # Package scheme name. For packages with multiple targets, it may be required to add `-Package` suffix. - # Example: your package is named `ClientService` and has two targets inside: `ClientServiceDTOs` and `ClientServiceAPI`. - # Then, your target would be `ClientService-Package`. - -s|--scheme) - SCHEME=${2} - shift 2 - ;; - *) - echo "Unknown parameter: '${1}'. Please use supported parameters: '-d|--device', '-r|--derived-data-path', '-s|--scheme'." - exit 1 - ;; - esac -done - -main From 38e904db8d8c27adf493065bb1e968dfc513b294 Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Mar 2025 12:25:20 +0100 Subject: [PATCH 12/17] Update runner and action versions --- .github/workflows/ci.yml | 13 ++++++------- .github/workflows/pr-check.yml | 7 +++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6172bfa..66efa3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,24 +3,23 @@ name: CI on: push: branches: [ "master" ] - paths: ['.github/workflows/**', 'Package.swift', 'Scripts/**', 'Sources/**', 'Tests/**'] env: - DEVELOPER_DIR: "/Applications/Xcode_14.3.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode_16.0.app/Contents/Developer" jobs: test: name: CI test - runs-on: macos-13 + runs-on: macos-15 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run tests run: sh ./Scripts/run_tests.sh - - name: Code coverage + - name: Get coverage run: sh ./Scripts/code_coverage.sh - name: Upload code coverage @@ -47,11 +46,11 @@ jobs: name: github-pages url: ${{ steps.deployment.outputs.page_url }} - runs-on: macos-13 + runs-on: macos-15 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build DocC run: sh ./Scripts/make_documentation.sh diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 8c63791..103781c 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -3,20 +3,19 @@ name: PR Check on: pull_request: branches: [ "master" ] - paths: ['.github/workflows/**', 'Package.swift', 'Scripts/**', 'Sources/**', 'Tests/**'] workflow_call: env: - DEVELOPER_DIR: "/Applications/Xcode_14.3.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode_16.0.app/Contents/Developer" jobs: test: name: Test - runs-on: macos-13 + runs-on: macos-15 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run tests run: sh ./Scripts/run_tests.sh From 54aa2e0c36cb928462f02f18581c9e1d2fb2dc7a Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Mar 2025 12:57:33 +0100 Subject: [PATCH 13/17] Modify `run_tests` script. Create Makefile as generic solution for calling methods --- .github/workflows/ci.yml | 2 +- .github/workflows/pr-check.yml | 2 +- Makefile | 14 +++++++++++++ Scripts/run_tests.sh | 37 +++++++++++++++++++++++++++++++++- 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 Makefile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66efa3f..e3beb2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v4 - name: Run tests - run: sh ./Scripts/run_tests.sh + run: make test -s - name: Get coverage run: sh ./Scripts/code_coverage.sh diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 103781c..796f0f5 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -18,4 +18,4 @@ jobs: uses: actions/checkout@v4 - name: Run tests - run: sh ./Scripts/run_tests.sh + run: make test -s diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b1566f5 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +build_dir := ./.build +derived_data_dir := $(build_dir)/derived_data +destination_device := platform=iOS Simulator,name=iPhone 16 +scheme := XCTestExtension +scripts_dir := ./Scripts +test_output_dir := $(build_dir)/test_output + +test: + echo "Testing..." + sh "$(scripts_dir)/run_tests.sh" \ + --derived-data-path "$($derived_data_dir)" \ + --device "$(destination_device)" \ + --output "$(test_output_dir)" \ + --scheme "$(scheme)" diff --git a/Scripts/run_tests.sh b/Scripts/run_tests.sh index 9a2ca03..300db3f 100755 --- a/Scripts/run_tests.sh +++ b/Scripts/run_tests.sh @@ -16,10 +16,45 @@ function package_clean() { } function swift_test() { - swift test --enable-code-coverage 2>&1 | xcpretty + xcodebuild \ + -default-test-execution-time-allowance 5 \ + -derivedDataPath "${DERIVED_DATA_PATH}" \ + -destination "${DESTINATION}" \ + -enableCodeCoverage "YES" \ + -jobs 4 \ + -parallel-testing-enabled "YES" \ + -resultBundlePath "${OUTPUT}" \ + -scheme "${SCHEME}" \ + -test-timeouts-enabled "YES" \ + test | xcbeautify } # ENTRY POINT +while [[ $# -gt 0 ]]; do + case $1 in + -d| --device) + DESTINATION=${2} + shift 2 + ;; + -o| --output) + OUTPUT=${2} + shift 2 + ;; + -p| --derived-data-path) + DERIVED_DATA_PATH=${2} + shift 2 + ;; + -s| --scheme) + SCHEME=${2} + shift 2 + ;; + *) + echo "Unknown argument: '${1}'" + exit 1 + ;; + esac +done + package_clean swift_test From a8812b180cb08bbdda22ce9219a58f7de1f8410c Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Mar 2025 14:14:15 +0100 Subject: [PATCH 14/17] Modify `code_coverage` script. Update Makefile --- .github/workflows/ci.yml | 2 +- Makefile | 11 +++++- Scripts/code_coverage.sh | 82 ++++++++++++++++++++++++++++------------ 3 files changed, 69 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3beb2e..1dfe511 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: run: make test -s - name: Get coverage - run: sh ./Scripts/code_coverage.sh + run: make code-coverage -s - name: Upload code coverage uses: codecov/codecov-action@v3 diff --git a/Makefile b/Makefile index b1566f5..753922d 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,21 @@ derived_data_dir := $(build_dir)/derived_data destination_device := platform=iOS Simulator,name=iPhone 16 scheme := XCTestExtension scripts_dir := ./Scripts +test_coverage_report_dir := $(build_dir)/coverage_reports test_output_dir := $(build_dir)/test_output +test_targets := XCTestExtensionTests test: echo "Testing..." sh "$(scripts_dir)/run_tests.sh" \ - --derived-data-path "$($derived_data_dir)" \ + --derived-data-path "$(derived_data_dir)" \ --device "$(destination_device)" \ --output "$(test_output_dir)" \ --scheme "$(scheme)" + +code-coverage: + echo "Gathering code coverage..." + sh "$(scripts_dir)/code_coverage.sh" \ + --derived-data-path "$(derived_data_dir)" \ + --output "$(test_coverage_report_dir)" \ + --test-targets $(test_targets) diff --git a/Scripts/code_coverage.sh b/Scripts/code_coverage.sh index 1e0aefe..b353e41 100644 --- a/Scripts/code_coverage.sh +++ b/Scripts/code_coverage.sh @@ -4,37 +4,71 @@ set -Eeuo pipefail source "$(dirname "$0")"/common.sh -# CONSTANTS +# FUNCTIONS -readonly COVERAGE_DIR_NAME=".coverage" -readonly SWIFT_BUILD_DIR_NAME=".build" -readonly PACKAGE_NAME=$(swift_package_name) -readonly PROFDATA_PATH="${SWIFT_BUILD_DIR_NAME}/debug/codecov/default.profdata" -readonly BIN_PATH="${SWIFT_BUILD_DIR_NAME}/debug/${PACKAGE_NAME}PackageTests.xctest/Contents/MacOS/${PACKAGE_NAME}PackageTests" +function main() { + local profdata + local ignore_regex + local test_targets -# FUNCTIONS + IFS=',' read -r -a test_targets <<< "$TEST_TARGETS" + profdata=$(find "$DERIVED_DATA_PATH" -name "Coverage.profdata") + ignore_regex=".build|Tests" -function print_code_coverage() { - xcrun llvm-cov report \ - "$BIN_PATH" \ - -instr-profile="$PROFDATA_PATH" \ - -ignore-filename-regex=".build|Tests" \ - -use-color -} + mkdir -p "$OUTPUT_DIR" + + for test_target in "${test_targets[@]}"; do + local coverage_report_path + local binary_path + local xctest_path -function export_code_coverage() { - local -r coverage_report_path="$COVERAGE_DIR_NAME/$PACKAGE_NAME.lcov" + coverage_report_path="$OUTPUT_DIR/$test_target.lcov" - mkdir -p "$COVERAGE_DIR_NAME" + printf '\n\nPreparing code coverage for %s...\n' "$test_target" - xcrun llvm-cov export \ - -format="lcov" "$BIN_PATH" \ - -instr-profile="$PROFDATA_PATH" \ - -ignore-filename-regex=".build|Tests" \ - > "$coverage_report_path" + xctest_path=$(find "$DERIVED_DATA_PATH" -name "$test_target.xctest") + if [[ -z "$xctest_path" ]]; then + echo "Error: XCTest bundle not found for target '$test_target'" + exit 1 + fi + + binary_path="$xctest_path/$test_target" + + # Print coverage + xcrun llvm-cov report "$binary_path" \ + -ignore-filename-regex="$ignore_regex" \ + -instr-profile="$profdata" \ + -use-color + + # Export coverage + xcrun llvm-cov show "$binary_path" \ + -ignore-filename-regex="$ignore_regex" \ + -instr-profile="$profdata" \ + > "$coverage_report_path" + done } # ENTRY POINT -print_code_coverage -export_code_coverage +while [[ $# -gt 0 ]]; do + case $1 in + -p| --derived-data-path) + DERIVED_DATA_PATH=${2} + shift 2 + ;; + -o| --output) + OUTPUT_DIR=${2} + shift 2 + ;; + -t| --test-targets) + TEST_TARGETS=${2} + shift 2 + ;; + *) + echo "Unknown argument: '${1}'" + exit 1 + ;; + esac +done + +main From 9d8003de5bc7895101d8989a20ae138780fde9af Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Mar 2025 15:03:39 +0100 Subject: [PATCH 15/17] Modify `make_documentation` script. Update Makefile --- .github/workflows/ci.yml | 2 +- Makefile | 12 +++++- Scripts/make_documentation.sh | 69 +++++++++++++++++++++++++---------- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1dfe511..58e6be8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,7 @@ jobs: uses: actions/checkout@v4 - name: Build DocC - run: sh ./Scripts/make_documentation.sh + run: make documentation -s - name: Upload artifact uses: actions/upload-pages-artifact@v1 diff --git a/Makefile b/Makefile index 753922d..816d0fd 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ build_dir := ./.build derived_data_dir := $(build_dir)/derived_data destination_device := platform=iOS Simulator,name=iPhone 16 +documentation_output_dir := ./docs scheme := XCTestExtension scripts_dir := ./Scripts test_coverage_report_dir := $(build_dir)/coverage_reports @@ -20,4 +21,13 @@ code-coverage: sh "$(scripts_dir)/code_coverage.sh" \ --derived-data-path "$(derived_data_dir)" \ --output "$(test_coverage_report_dir)" \ - --test-targets $(test_targets) + --test-targets "$(test_targets)" + +documentation: + echo "Generating documentation..." + sh "$(scripts_dir)/make_documentation.sh" \ + --derived-data-path "$(derived_data_dir)" \ + --device "$(destination_device)" \ + --hosting-base-path "$(scheme)" \ + --output "$(documentation_output_dir)" \ + --scheme "$(scheme)" diff --git a/Scripts/make_documentation.sh b/Scripts/make_documentation.sh index 60451a0..7cc2382 100644 --- a/Scripts/make_documentation.sh +++ b/Scripts/make_documentation.sh @@ -4,31 +4,62 @@ set -Eeuo pipefail source "$(dirname "$0")"/common.sh -# CONSTANTS +# FUNCTIONS -readonly DOCS_PATH="./docs" -readonly PACKAGE_NAME=$(swift_package_name) +function main() { + local -r docs_index_path="$OUTPUT_DIR/index.html" + local -r package_path_element=$(lowercase "$SCHEME") + local docc_archive_path -# FUNCTIONS + echo "Creating doccarchive..." + xcodebuild \ + -derivedDataPath "$DERIVED_DATA_PATH" \ + -destination "$DESTINATION" \ + -scheme "$SCHEME" \ + docbuild | xcbeautify + echo "Done!" -function create_documentation() { - local -r docs_index_path="$DOCS_PATH/index.html" - local -r package_path_element=$(lowercase "$PACKAGE_NAME") - - echo "$package_path_element" - swift package \ - --allow-writing-to-directory "$DOCS_PATH" \ - generate-documentation \ - --disable-indexing \ - --hosting-base-path "$PACKAGE_NAME" \ - --include-extended-types \ - --output-path "$DOCS_PATH" \ - --target "$PACKAGE_NAME" \ - --transform-for-static-hosting + docc_archive_path=$(find "$DERIVED_DATA_PATH" -name "$SCHEME.doccarchive") + echo "DocC archive found at path '$docc_archive_path'." + + echo "Converting doccarchive to static HTML..." + "$(xcrun --find docc)" process-archive transform-for-static-hosting "$docc_archive_path" \ + --hosting-base-path "$HOSTING_BASE_PATH" \ + --output-path "$OUTPUT_DIR" echo "" > "$docs_index_path" + echo "Done!" } # ENTRY POINT -create_documentation +while [[ $# -gt 0 ]]; do + case $1 in + -b| --hosting-base-path) + HOSTING_BASE_PATH=${2} + shift 2 + ;; + -d| --device) + DESTINATION=${2} + shift 2 + ;; + -p| --derived-data-path) + DERIVED_DATA_PATH=${2} + shift 2 + ;; + -o| --output) + OUTPUT_DIR=${2} + shift 2 + ;; + -s| --scheme) + SCHEME=${2} + shift 2 + ;; + *) + echo "Unknown argument: '${1}'" + exit 1 + ;; + esac +done + +main From 9b8756563409a2570ff333e8bf5c11254c9d63d6 Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Mar 2025 15:13:53 +0100 Subject: [PATCH 16/17] test envs --- .github/workflows/action-test.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/action-test.yml b/.github/workflows/action-test.yml index f793bb5..3fcaddf 100644 --- a/.github/workflows/action-test.yml +++ b/.github/workflows/action-test.yml @@ -6,6 +6,8 @@ on: env: DEVELOPER_DIR: "/Applications/Xcode_16.0.app/Contents/Developer" + TARGET_DEVICE: "platform=iOS Simulator,name=iPhone 16,OS=18.0" + TARGET_SCHEME: "XCTestExtension" jobs: test-custom-action: @@ -23,8 +25,8 @@ jobs: id: test-action uses: Filozoff/action-swift-propose-next-version@v1 with: - device: "platform=iOS Simulator,name=iPhone 16,OS=18.0" - scheme: "XCTestExtension" + device: "${{ env.TARGET_DEVICE }}" + scheme: "${{ env.TARGET_SCHEME }}" push-tag: name: Create and push tag From e5e63484ba201cd04c45dba9dbdaa6ab2864c40f Mon Sep 17 00:00:00 2001 From: Kamil Wyszomierski Date: Fri, 14 Mar 2025 15:21:32 +0100 Subject: [PATCH 17/17] Move next version tag creation actions to CI configuration --- .github/workflows/action-test.yml | 44 ------------------------------- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 44 deletions(-) delete mode 100644 .github/workflows/action-test.yml diff --git a/.github/workflows/action-test.yml b/.github/workflows/action-test.yml deleted file mode 100644 index 3fcaddf..0000000 --- a/.github/workflows/action-test.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Custom github action test - -on: - push: - branches: [] - -env: - DEVELOPER_DIR: "/Applications/Xcode_16.0.app/Contents/Developer" - TARGET_DEVICE: "platform=iOS Simulator,name=iPhone 16,OS=18.0" - TARGET_SCHEME: "XCTestExtension" - -jobs: - test-custom-action: - name: Test custom action - runs-on: macos-15 - outputs: - next-version: ${{ steps.test-action.outputs.proposed-next-version }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: '0' - - - name: Test custom action - id: test-action - uses: Filozoff/action-swift-propose-next-version@v1 - with: - device: "${{ env.TARGET_DEVICE }}" - scheme: "${{ env.TARGET_SCHEME }}" - - push-tag: - name: Create and push tag - needs: test-custom-action - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Push tag - uses: Filozoff/action-make-tag@v1 - with: - commit-sha: "${{ github.sha }}" - tag: ${{ needs.test-custom-action.outputs.next-version }} - token: ${{ secrets.TAG_TOKEN }} - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58e6be8..8a30e34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,8 @@ on: env: DEVELOPER_DIR: "/Applications/Xcode_16.0.app/Contents/Developer" + TARGET_DEVICE: "platform=iOS Simulator,name=iPhone 16,OS=18.0" + TARGET_SCHEME: "XCTestExtension" jobs: test: @@ -30,6 +32,38 @@ jobs: fail_ci_if_error: true verbose: true + propose-next-version: + name: Propose next version + runs-on: macos-15 + outputs: + next-version: ${{ steps.next-version.outputs.proposed-next-version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + + - name: Get next version + id: next-version + uses: Filozoff/action-swift-propose-next-version@v1 + with: + device: "${{ env.TARGET_DEVICE }}" + scheme: "${{ env.TARGET_SCHEME }}" + + push-tag: + name: Create and push tag + needs: propose-next-version + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Push tag + uses: Filozoff/action-make-tag@v1 + with: + commit-sha: "${{ github.sha }}" + tag: ${{ needs.propose-next-version.outputs.next-version }} + token: ${{ secrets.TAG_TOKEN }} + documentation: name: Deploy documentation needs: test