diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6172bfa..8a30e34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,25 +3,26 @@ 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" + TARGET_DEVICE: "platform=iOS Simulator,name=iPhone 16,OS=18.0" + TARGET_SCHEME: "XCTestExtension" 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 + run: make test -s - - name: Code coverage - run: sh ./Scripts/code_coverage.sh + - name: Get coverage + run: make code-coverage -s - name: Upload code coverage uses: codecov/codecov-action@v3 @@ -31,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 @@ -47,14 +80,14 @@ 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 + run: make documentation -s - name: Upload artifact uses: actions/upload-pages-artifact@v1 diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 8c63791..796f0f5 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 + run: make test -s diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..816d0fd --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +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 +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)" \ + --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)" + +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/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 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 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