From ef8664aacfe2392bbd637a47f38c64eb862253bb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 00:54:15 +0000 Subject: [PATCH 1/6] Initial plan From 621d1fa583491b471ca4b854b5db4675aa1c73c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 01:03:34 +0000 Subject: [PATCH 2/6] Add integration tests and improve path detection Co-authored-by: MartinP7r <2669027+MartinP7r@users.noreply.github.com> --- .github/workflows/integration-tests.yml | 50 +++++++ Example/ackgen.sh | 14 +- README.md | 40 +++++- Tests/AckGenTests/IntegrationTests.swift | 165 +++++++++++++++++++++++ diagnose_path.sh | 110 +++++++++++++++ 5 files changed, 371 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/integration-tests.yml create mode 100644 Tests/AckGenTests/IntegrationTests.swift create mode 100755 diagnose_path.sh diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml new file mode 100644 index 0000000..57decf3 --- /dev/null +++ b/.github/workflows/integration-tests.yml @@ -0,0 +1,50 @@ +name: Integration Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + integration-test: + name: Integration Tests + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build CLI + run: swift build --product ackgen + + - name: Run Integration Tests + run: swift test --filter IntegrationTests + + - name: Test Example Project Setup + run: | + cd Example + + # Simulate a real Xcode project environment + export SRCROOT="${PWD}" + export PROJECT_TEMP_DIR="${PWD}/Build/Intermediates.noindex/AckGenExample.build" + + # Create mock directory structure + mkdir -p "${PROJECT_TEMP_DIR}" + mkdir -p "Build/DerivedData/AckGenExample-test/SourcePackages/checkouts/AckGen" + + # The path calculation should work + CALCULATED_PATH=$(echo "${PROJECT_TEMP_DIR}" | awk -F'/Build/' '{print $1}' | xargs -I {} echo "{}/SourcePackages/checkouts") + echo "Calculated package path: ${CALCULATED_PATH}" + + # Verify the calculation matches expected structure + if [[ "${CALCULATED_PATH}" == *"/SourcePackages/checkouts" ]]; then + echo "✅ Path calculation works correctly" + else + echo "❌ Path calculation failed" + exit 1 + fi diff --git a/Example/ackgen.sh b/Example/ackgen.sh index 2530de5..90ed84e 100644 --- a/Example/ackgen.sh +++ b/Example/ackgen.sh @@ -1,10 +1,14 @@ -# DIR=$PROJECT_TEMP_DIR/../../../SourcePackages/checkouts/AckGen -# Different path, because the sample uses AckGen as a local package: +# For the Example app, AckGen is used as a local package, so we use a simple relative path +# In your own project with SPM, use the dynamic path calculation from the README: +# BASE_DIR=$(echo "$PROJECT_TEMP_DIR" | awk -F'/Build/' '{print $1}') +# DIR="$BASE_DIR/SourcePackages/checkouts/AckGen" DIR=.. + if [ -d "$DIR" ]; then - cd $DIR - SDKROOT=(xcrun --sdk macosx --show-sdk-path) - swift run ackgen $SRCROOT/PackageLicenses.plist + cd "$DIR" + SDKROOT=$(xcrun --sdk macosx --show-sdk-path) + swift run ackgen "$SRCROOT/PackageLicenses.plist" else echo "warning: AckGen not found. Please install the package via SPM (https://github.com/MartinP7r/AckGen#installation)" fi + diff --git a/README.md b/README.md index e865481..17305a9 100644 --- a/README.md +++ b/README.md @@ -25,16 +25,22 @@ This can be used to feed a SwiftUI List or UITableView dataSource in your app. 2. Add the following as a Run Script for your target in Xcode ```sh -DIR=$PROJECT_TEMP_DIR/../../../SourcePackages/checkouts/AckGen +# Calculate the package path dynamically (works with various Xcode configurations) +BASE_DIR=$(echo "$PROJECT_TEMP_DIR" | awk -F'/Build/' '{print $1}') +DIR="$BASE_DIR/SourcePackages/checkouts/AckGen" + if [ -d "$DIR" ]; then - cd $DIR - SDKROOT=(xcrun --sdk macosx --show-sdk-path) + cd "$DIR" + SDKROOT=$(xcrun --sdk macosx --show-sdk-path) swift run ackgen else echo "warning: AckGen not found. Please install the package via SPM (https://github.com/MartinP7r/AckGen#installation)" fi ``` +> **Note** +> The script dynamically calculates the package path from `PROJECT_TEMP_DIR` to support various Xcode project configurations. If you encounter issues, run `./diagnose_path.sh` from this repository to debug your setup. + Make sure to set `ENABLE_USER_SCRIPT_SANDBOXING` to `NO` in your build settings so the build phase above can write to the desired destination. If you want the plist file to be saved somewhere other than `Acknowledgements.plist` at the root of your project (`$SRCROOT/Acknowledgements.plist`), you can provide a custom path as the first command line argument to `ackgen` above. @@ -76,6 +82,34 @@ struct ContentView: View { Until 1.0 is reached, minor versions will be breaking. +## Troubleshooting + +### "AckGen not found" Error + +If you see the warning `AckGen not found. Please install the package via SPM`, try the following: + +1. **Run the diagnostic script**: Execute `./diagnose_path.sh` from this repository (set `PROJECT_TEMP_DIR` to your Xcode build directory path) +2. **Verify SPM installation**: Make sure AckGen is added as a Swift Package dependency in your Xcode project +3. **Build your project**: SPM dependencies are only downloaded after you build your project at least once +4. **Check your path**: The script in the README dynamically calculates the package path. If your project uses a non-standard structure, you may need to adjust the path calculation + +#### Understanding Path Detection + +Xcode places SPM packages in different locations depending on your project setup. The recommended script uses: + +```sh +BASE_DIR=$(echo "$PROJECT_TEMP_DIR" | awk -F'/Build/' '{print $1}') +DIR="$BASE_DIR/SourcePackages/checkouts/AckGen" +``` + +This extracts the base directory before `/Build/` and appends the standard SPM checkout path. This approach works across: +- Different Xcode versions +- Debug/Release configurations +- iOS/macOS/watchOS/tvOS targets +- Simulator/Device builds + +If you need a different path for your setup (e.g., local package development), you can modify the `DIR` variable accordingly. See `Example/ackgen.sh` for an example of using a local package. + ## Contribution This is my first stab at building a Swift package and was mainly intended to be an exercise. diff --git a/Tests/AckGenTests/IntegrationTests.swift b/Tests/AckGenTests/IntegrationTests.swift new file mode 100644 index 0000000..dce4581 --- /dev/null +++ b/Tests/AckGenTests/IntegrationTests.swift @@ -0,0 +1,165 @@ +import XCTest +import Foundation + +/// Integration tests for AckGen CLI path detection and package discovery +final class IntegrationTests: XCTestCase { + + var tempDir: URL! + + override func setUp() { + super.setUp() + tempDir = FileManager.default.temporaryDirectory + .appendingPathComponent("AckGenIntegrationTests-\(UUID().uuidString)") + try? FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) + } + + override func tearDown() { + if let tempDir = tempDir { + try? FileManager.default.removeItem(at: tempDir) + } + super.tearDown() + } + + /// Test that the path calculation logic correctly extracts the base path from PROJECT_TEMP_DIR + func testPathCalculationFromProjectTempDir() { + // Given: Various PROJECT_TEMP_DIR patterns used by Xcode + let testCases: [(input: String, expectedBase: String)] = [ + // Standard SPM project structure + ( + "/Users/username/Library/Developer/Xcode/DerivedData/AppName-xyz/Build/Intermediates.noindex/AppName.build", + "/Users/username/Library/Developer/Xcode/DerivedData/AppName-xyz" + ), + // Nested build directory + ( + "/Users/username/Library/Developer/Xcode/DerivedData/AppName-abc/Build/Products/Debug/AppName.build", + "/Users/username/Library/Developer/Xcode/DerivedData/AppName-abc" + ), + // Different user paths + ( + "/Users/developer/Library/Developer/Xcode/DerivedData/MyProject-def/Build/Intermediates.noindex/MyProject.build", + "/Users/developer/Library/Developer/Xcode/DerivedData/MyProject-def" + ), + ] + + // When & Then: Path calculation should correctly extract base directory + for testCase in testCases { + let calculatedBase = testCase.input.components(separatedBy: "/Build/")[0] + let expectedPackagePath = calculatedBase + "/SourcePackages/checkouts" + let expectedBasePath = testCase.expectedBase + "/SourcePackages/checkouts" + + XCTAssertEqual(expectedPackagePath, expectedBasePath, + "Path calculation failed for \(testCase.input)") + } + } + + /// Test that the relative path approach from README matches the calculated path + func testRelativePathVsCalculatedPath() { + // Given: A simulated PROJECT_TEMP_DIR from Xcode + let projectTempDir = "/Users/username/Library/Developer/Xcode/DerivedData/AppName-xyz/Build/Intermediates.noindex/AppName.build" + + // When: Calculate path using the CLI approach + let calculatedPath = projectTempDir.components(separatedBy: "/Build/")[0] + "/SourcePackages/checkouts" + + // Expected path from base directory + let expectedPath = "/Users/username/Library/Developer/Xcode/DerivedData/AppName-xyz/SourcePackages/checkouts" + + // Then: Paths should match + XCTAssertEqual(calculatedPath, expectedPath) + + // The relative path "../../../" from PROJECT_TEMP_DIR would give: + // ../../../ from /Users/username/Library/Developer/Xcode/DerivedData/AppName-xyz/Build/Intermediates.noindex/AppName.build + // should reach: /Users/username/Library/Developer/Xcode/DerivedData/AppName-xyz/Build + // But we need: /Users/username/Library/Developer/Xcode/DerivedData/AppName-xyz + + // This demonstrates why the relative path approach may fail + let url = URL(fileURLWithPath: projectTempDir) + let relativeUrl = url + .deletingLastPathComponent() // Remove AppName.build + .deletingLastPathComponent() // Remove Intermediates.noindex + .deletingLastPathComponent() // Remove Build + + let relativeBasePath = relativeUrl.path + "/SourcePackages/checkouts" + XCTAssertEqual(relativeBasePath, expectedPath, + "Relative path should match calculated path") + } + + /// Integration test: Create a mock package structure and verify discovery + func testPackageDiscoveryWithMockStructure() throws { + // Given: Create a mock SourcePackages/checkouts structure + let derivedDataDir = tempDir.appendingPathComponent("DerivedData/TestApp-abc") + let buildDir = derivedDataDir.appendingPathComponent("Build/Intermediates.noindex/TestApp.build") + let packagesDir = derivedDataDir.appendingPathComponent("SourcePackages/checkouts") + + try FileManager.default.createDirectory(at: buildDir, withIntermediateDirectories: true) + try FileManager.default.createDirectory(at: packagesDir, withIntermediateDirectories: true) + + // Create mock package directories with LICENSE files + let package1 = packagesDir.appendingPathComponent("TestPackage1") + let package2 = packagesDir.appendingPathComponent("TestPackage2") + + try FileManager.default.createDirectory(at: package1, withIntermediateDirectories: true) + try FileManager.default.createDirectory(at: package2, withIntermediateDirectories: true) + + let license1 = "MIT License\nCopyright (c) 2024 Test Package 1" + let license2 = "Apache License 2.0\nCopyright (c) 2024 Test Package 2" + + try license1.write(to: package1.appendingPathComponent("LICENSE"), atomically: true, encoding: .utf8) + try license2.write(to: package2.appendingPathComponent("LICENSE.txt"), atomically: true, encoding: .utf8) + + // When: Calculate package cache path from PROJECT_TEMP_DIR + let projectTempDir = buildDir.path + let calculatedPackagePath = projectTempDir.components(separatedBy: "/Build/")[0] + "/SourcePackages/checkouts" + + // Then: Should find the correct directory + XCTAssertEqual(calculatedPackagePath, packagesDir.path) + XCTAssertTrue(FileManager.default.fileExists(atPath: calculatedPackagePath)) + + // Should discover package directories + let discoveredPackages = try FileManager.default.contentsOfDirectory(atPath: calculatedPackagePath) + XCTAssertEqual(discoveredPackages.sorted(), ["TestPackage1", "TestPackage2"]) + } + + /// Test edge case: PROJECT_TEMP_DIR with multiple "Build" components + func testPathCalculationWithMultipleBuildComponents() { + // Given: A PROJECT_TEMP_DIR that contains "Build" multiple times + let projectTempDir = "/Users/Build/Projects/AppName-xyz/Build/Intermediates.noindex/AppName.build" + + // When: Calculate path (should split on first "/Build/") + let calculatedPath = projectTempDir.components(separatedBy: "/Build/")[0] + "/SourcePackages/checkouts" + + // Then: Should correctly identify the base path before the first Build + let expectedPath = "/Users/Build/Projects/AppName-xyz/SourcePackages/checkouts" + XCTAssertEqual(calculatedPath, expectedPath) + } + + /// Test that the path calculation handles various Xcode project configurations + func testPathCalculationForDifferentXcodeConfigurations() { + let configurations: [(name: String, tempDir: String, expectedBase: String)] = [ + ( + "Standard Debug Build", + "/Users/dev/Library/Developer/Xcode/DerivedData/App-xyz/Build/Intermediates.noindex/App.build/Debug-iphonesimulator/App.build", + "/Users/dev/Library/Developer/Xcode/DerivedData/App-xyz" + ), + ( + "Release Build", + "/Users/dev/Library/Developer/Xcode/DerivedData/App-xyz/Build/Intermediates.noindex/App.build/Release-iphoneos/App.build", + "/Users/dev/Library/Developer/Xcode/DerivedData/App-xyz" + ), + ( + "macOS Build", + "/Users/dev/Library/Developer/Xcode/DerivedData/MacApp-abc/Build/Intermediates.noindex/MacApp.build/Debug/MacApp.build", + "/Users/dev/Library/Developer/Xcode/DerivedData/MacApp-abc" + ), + ] + + for config in configurations { + // When: Calculate package path + let calculatedPath = config.tempDir.components(separatedBy: "/Build/")[0] + "/SourcePackages/checkouts" + let expectedPath = config.expectedBase + "/SourcePackages/checkouts" + + // Then: Should correctly calculate path + XCTAssertEqual(calculatedPath, expectedPath, + "Failed for configuration: \(config.name)") + } + } +} diff --git a/diagnose_path.sh b/diagnose_path.sh new file mode 100755 index 0000000..24fe5a6 --- /dev/null +++ b/diagnose_path.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# Script to diagnose AckGen path detection issues +# Run this from your Xcode Run Script phase or manually with environment variables set + +set -e + +echo "🔍 AckGen Path Diagnostics" +echo "==========================" +echo "" + +# Check if we're running in an Xcode environment +if [ -z "$PROJECT_TEMP_DIR" ]; then + echo "❌ ERROR: PROJECT_TEMP_DIR is not set" + echo "This script must be run from an Xcode Run Script phase or with PROJECT_TEMP_DIR set" + echo "" + echo "To test manually, set PROJECT_TEMP_DIR to your Xcode build directory, e.g.:" + echo "export PROJECT_TEMP_DIR=/Users/\$USER/Library/Developer/Xcode/DerivedData/YourApp-xyz/Build/Intermediates.noindex/YourApp.build" + exit 1 +fi + +echo "📂 Environment Variables:" +echo " PROJECT_TEMP_DIR: $PROJECT_TEMP_DIR" +echo " SRCROOT: ${SRCROOT:-not set}" +echo "" + +# Calculate package path using the same logic as AckGen CLI +CALCULATED_BASE=$(echo "$PROJECT_TEMP_DIR" | awk -F'/Build/' '{print $1}') +CALCULATED_PACKAGE_PATH="${CALCULATED_BASE}/SourcePackages/checkouts" + +echo "🧮 Path Calculation:" +echo " Base directory: $CALCULATED_BASE" +echo " Package path: $CALCULATED_PACKAGE_PATH" +echo "" + +# Check using relative path approach (from README) +RELATIVE_BASE="$PROJECT_TEMP_DIR/../../../" +RELATIVE_PACKAGE_PATH="${RELATIVE_BASE}/SourcePackages/checkouts" + +echo "📍 Relative Path Approach (from README):" +echo " Relative base: $RELATIVE_BASE" +echo " Package path: $RELATIVE_PACKAGE_PATH" +echo "" + +# Verify both approaches +echo "✅ Verification:" +if [ -d "$CALCULATED_PACKAGE_PATH" ]; then + echo " ✓ Calculated path exists: $CALCULATED_PACKAGE_PATH" + PACKAGES=$(ls -1 "$CALCULATED_PACKAGE_PATH" 2>/dev/null | grep -v "^\." || echo "") + if [ -n "$PACKAGES" ]; then + echo " ✓ Found packages:" + echo "$PACKAGES" | sed 's/^/ - /' + else + echo " ⚠ No packages found in $CALCULATED_PACKAGE_PATH" + fi +else + echo " ✗ Calculated path does not exist: $CALCULATED_PACKAGE_PATH" +fi + +if [ -d "$RELATIVE_PACKAGE_PATH" ]; then + echo " ✓ Relative path exists: $RELATIVE_PACKAGE_PATH" +else + echo " ✗ Relative path does not exist: $RELATIVE_PACKAGE_PATH" +fi + +# Check for AckGen specifically +ACKGEN_CALCULATED="${CALCULATED_PACKAGE_PATH}/AckGen" +ACKGEN_RELATIVE="${RELATIVE_PACKAGE_PATH}/AckGen" + +echo "" +echo "🔎 AckGen Package Location:" +if [ -d "$ACKGEN_CALCULATED" ]; then + echo " ✓ Found via calculated path: $ACKGEN_CALCULATED" +elif [ -d "$ACKGEN_RELATIVE" ]; then + echo " ✓ Found via relative path: $ACKGEN_RELATIVE" +else + echo " ✗ AckGen package not found in either location" + echo "" + echo "💡 Troubleshooting:" + echo " 1. Make sure AckGen is added as a Swift Package dependency in Xcode" + echo " 2. Build your project at least once so SPM downloads dependencies" + echo " 3. Check if packages are in a different location:" + echo " find ~/Library/Developer/Xcode/DerivedData -name AckGen -type d 2>/dev/null" +fi + +echo "" +echo "📋 Summary:" +if [ -d "$ACKGEN_CALCULATED" ] || [ -d "$ACKGEN_RELATIVE" ]; then + echo " ✅ AckGen package is accessible" + echo "" + echo "Use this in your Run Script:" + echo "┌─────────────────────────────────────────────────────────────" + if [ -d "$ACKGEN_CALCULATED" ]; then + echo "│ DIR=${CALCULATED_PACKAGE_PATH}/AckGen" + else + echo "│ DIR=${RELATIVE_PACKAGE_PATH}/AckGen" + fi + echo "│ if [ -d \"\$DIR\" ]; then" + echo "│ cd \$DIR" + echo "│ SDKROOT=\$(xcrun --sdk macosx --show-sdk-path)" + echo "│ swift run ackgen" + echo "│ else" + echo "│ echo \"warning: AckGen not found at \$DIR\"" + echo "│ fi" + echo "└─────────────────────────────────────────────────────────────" +else + echo " ❌ AckGen package could not be found" + echo " Please verify that AckGen is installed via Swift Package Manager" +fi + +echo "" From 990fe43091a073ca5337dfc404b48e0875681577 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 01:07:18 +0000 Subject: [PATCH 3/6] Add Swift diagnostic tool and fix path calculation edge case Co-authored-by: MartinP7r <2669027+MartinP7r@users.noreply.github.com> --- README.md | 22 +++-- Sources/AckGenCLI/AckGen.swift | 12 ++- diagnose_path.sh | 4 +- diagnose_path.swift | 151 +++++++++++++++++++++++++++++++++ test_path_calculation.sh | 82 ++++++++++++++++++ 5 files changed, 260 insertions(+), 11 deletions(-) create mode 100755 diagnose_path.swift create mode 100755 test_path_calculation.sh diff --git a/README.md b/README.md index 17305a9..89b5f7e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,8 @@ This can be used to feed a SwiftUI List or UITableView dataSource in your app. ```sh # Calculate the package path dynamically (works with various Xcode configurations) -BASE_DIR=$(echo "$PROJECT_TEMP_DIR" | awk -F'/Build/' '{print $1}') +# Use parameter expansion to remove everything from /Build/ onwards +BASE_DIR="${PROJECT_TEMP_DIR%/Build/*}" DIR="$BASE_DIR/SourcePackages/checkouts/AckGen" if [ -d "$DIR" ]; then @@ -88,7 +89,9 @@ Until 1.0 is reached, minor versions will be breaking. If you see the warning `AckGen not found. Please install the package via SPM`, try the following: -1. **Run the diagnostic script**: Execute `./diagnose_path.sh` from this repository (set `PROJECT_TEMP_DIR` to your Xcode build directory path) +1. **Run the diagnostic script**: + - Swift version: `swift diagnose_path.swift` (set `PROJECT_TEMP_DIR` environment variable first) + - Shell version: `./diagnose_path.sh` (set `PROJECT_TEMP_DIR` environment variable first) 2. **Verify SPM installation**: Make sure AckGen is added as a Swift Package dependency in your Xcode project 3. **Build your project**: SPM dependencies are only downloaded after you build your project at least once 4. **Check your path**: The script in the README dynamically calculates the package path. If your project uses a non-standard structure, you may need to adjust the path calculation @@ -98,15 +101,18 @@ If you see the warning `AckGen not found. Please install the package via SPM`, t Xcode places SPM packages in different locations depending on your project setup. The recommended script uses: ```sh -BASE_DIR=$(echo "$PROJECT_TEMP_DIR" | awk -F'/Build/' '{print $1}') +BASE_DIR="${PROJECT_TEMP_DIR%/Build/*}" DIR="$BASE_DIR/SourcePackages/checkouts/AckGen" ``` -This extracts the base directory before `/Build/` and appends the standard SPM checkout path. This approach works across: -- Different Xcode versions -- Debug/Release configurations -- iOS/macOS/watchOS/tvOS targets -- Simulator/Device builds +This uses bash parameter expansion to remove everything from `/Build/` onwards (including `/Build/` itself) and appends the standard SPM checkout path. This approach: +- Works across different Xcode versions +- Handles Debug/Release configurations +- Supports iOS/macOS/watchOS/tvOS targets +- Works with Simulator/Device builds +- Handles edge cases like usernames or project names containing "Build" + +The calculation finds the **last** occurrence of `/Build/` in the path, ensuring correct behavior even when "Build" appears elsewhere in the path (e.g., `/Users/Build/Projects/MyApp/Build/Intermediates`). If you need a different path for your setup (e.g., local package development), you can modify the `DIR` variable accordingly. See `Example/ackgen.sh` for an example of using a local package. diff --git a/Sources/AckGenCLI/AckGen.swift b/Sources/AckGenCLI/AckGen.swift index ad75fd6..57c1eb5 100644 --- a/Sources/AckGenCLI/AckGen.swift +++ b/Sources/AckGenCLI/AckGen.swift @@ -32,7 +32,17 @@ struct AckGenCLI { let settingsTitle: String = arguments.count > 2 ? arguments[2] : "Acknowledgements" - let packageCachePath = tempDirPath.components(separatedBy: "/Build/")[0] + "/SourcePackages/checkouts" + // Calculate package cache path using improved logic + // Find the last occurrence of "/Build/" to handle edge cases like "Build" in username + let packageCachePath: String + if let range = tempDirPath.range(of: "/Build/", options: .backwards) { + let basePath = String(tempDirPath[../dev/null") + } + + print("") + print("📋 Summary:") + + if ackgenFound { + print(" ✅ AckGen package is accessible") + print("") + print("Use this in your Run Script:") + print("┌─────────────────────────────────────────────────────────────") + print("│ # Calculate the package path dynamically") + print("│ BASE_DIR=$(echo \"$PROJECT_TEMP_DIR\" | awk -F'/Build/' '{print $1}')") + print("│ DIR=\"$BASE_DIR/SourcePackages/checkouts/AckGen\"") + print("│ ") + print("│ if [ -d \"$DIR\" ]; then") + print("│ cd \"$DIR\"") + print("│ SDKROOT=$(xcrun --sdk macosx --show-sdk-path)") + print("│ swift run ackgen") + print("│ else") + print("│ echo \"warning: AckGen not found at $DIR\"") + print("│ fi") + print("└─────────────────────────────────────────────────────────────") + } else { + print(" ❌ AckGen package could not be found") + print(" Please verify that AckGen is installed via Swift Package Manager") + } + + print("") + + // Additional diagnostics + if calculatedPathExists && !foundPackages.contains("AckGen") { + print("⚠️ Note: Package directory exists but AckGen is not found.") + print("Found packages: \(foundPackages.joined(separator: ", "))") + print("Make sure AckGen is listed in your Xcode project's Swift Packages.") + } + } +} + +PathDiagnostics.main() diff --git a/test_path_calculation.sh b/test_path_calculation.sh new file mode 100755 index 0000000..ba41425 --- /dev/null +++ b/test_path_calculation.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# Test script to verify path calculation works correctly +# This mimics what the Run Script phase does + +echo "Testing AckGen Path Calculation" +echo "================================" +echo "" + +# Test cases with different PROJECT_TEMP_DIR structures +test_cases=( + "/Users/username/Library/Developer/Xcode/DerivedData/AppName-xyz/Build/Intermediates.noindex/AppName.build" + "/Users/dev/Library/Developer/Xcode/DerivedData/App-xyz/Build/Intermediates.noindex/App.build/Debug-iphonesimulator/App.build" + "/Users/Build/Projects/AppName-xyz/Build/Intermediates.noindex/AppName.build" +) + +expected_bases=( + "/Users/username/Library/Developer/Xcode/DerivedData/AppName-xyz" + "/Users/dev/Library/Developer/Xcode/DerivedData/App-xyz" + "/Users/Build/Projects/AppName-xyz" +) + +passed=0 +failed=0 + +for i in "${!test_cases[@]}"; do + PROJECT_TEMP_DIR="${test_cases[$i]}" + EXPECTED_BASE="${expected_bases[$i]}" + + # Calculate base directory (same logic as in README - improved version) + BASE_DIR="${PROJECT_TEMP_DIR%/Build/*}" + PACKAGE_PATH="$BASE_DIR/SourcePackages/checkouts" + + EXPECTED_PACKAGE_PATH="$EXPECTED_BASE/SourcePackages/checkouts" + + if [ "$PACKAGE_PATH" = "$EXPECTED_PACKAGE_PATH" ]; then + echo "✅ Test $((i+1)): PASSED" + echo " Input: $PROJECT_TEMP_DIR" + echo " Output: $PACKAGE_PATH" + passed=$((passed + 1)) + else + echo "❌ Test $((i+1)): FAILED" + echo " Input: $PROJECT_TEMP_DIR" + echo " Expected: $EXPECTED_PACKAGE_PATH" + echo " Got: $PACKAGE_PATH" + failed=$((failed + 1)) + fi + echo "" +done + +# Test edge case: multiple "Build" in path +echo "Testing edge case: Multiple 'Build' in path" +PROJECT_TEMP_DIR="/Users/Build/Projects/AppName-xyz/Build/Intermediates.noindex/AppName.build" +BASE_DIR="${PROJECT_TEMP_DIR%/Build/*}" +EXPECTED="/Users/Build/Projects/AppName-xyz" + +if [ "$BASE_DIR" = "$EXPECTED" ]; then + echo "✅ Edge case: PASSED (correctly uses first /Build/)" + echo " Input: $PROJECT_TEMP_DIR" + echo " Output: $BASE_DIR" + passed=$((passed + 1)) +else + echo "❌ Edge case: FAILED" + echo " Expected: $EXPECTED" + echo " Got: $BASE_DIR" + failed=$((failed + 1)) +fi +echo "" + +# Summary +echo "================================" +echo "Test Summary:" +echo " Passed: $passed" +echo " Failed: $failed" +echo "" + +if [ $failed -eq 0 ]; then + echo "✅ All tests passed!" + exit 0 +else + echo "❌ Some tests failed" + exit 1 +fi From c439bbd2f0bf4ff4d239df046a07da6f5a99c6e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 01:15:03 +0000 Subject: [PATCH 4/6] Fix integration tests to use improved path calculation logic Co-authored-by: MartinP7r <2669027+MartinP7r@users.noreply.github.com> --- .github/workflows/integration-tests.yml | 5 ++- Example/ackgen.sh | 2 +- Tests/AckGenTests/IntegrationTests.swift | 50 +++++++++++++++++++----- diagnose_path.swift | 5 ++- test_path_calculation.sh | 2 +- 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 57decf3..8f07c96 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -37,8 +37,9 @@ jobs: mkdir -p "${PROJECT_TEMP_DIR}" mkdir -p "Build/DerivedData/AckGenExample-test/SourcePackages/checkouts/AckGen" - # The path calculation should work - CALCULATED_PATH=$(echo "${PROJECT_TEMP_DIR}" | awk -F'/Build/' '{print $1}' | xargs -I {} echo "{}/SourcePackages/checkouts") + # The path calculation should work (using the improved logic) + BASE_DIR="${PROJECT_TEMP_DIR%/Build/*}" + CALCULATED_PATH="${BASE_DIR}/SourcePackages/checkouts" echo "Calculated package path: ${CALCULATED_PATH}" # Verify the calculation matches expected structure diff --git a/Example/ackgen.sh b/Example/ackgen.sh index 90ed84e..43535ee 100644 --- a/Example/ackgen.sh +++ b/Example/ackgen.sh @@ -1,6 +1,6 @@ # For the Example app, AckGen is used as a local package, so we use a simple relative path # In your own project with SPM, use the dynamic path calculation from the README: -# BASE_DIR=$(echo "$PROJECT_TEMP_DIR" | awk -F'/Build/' '{print $1}') +# BASE_DIR="${PROJECT_TEMP_DIR%/Build/*}" # DIR="$BASE_DIR/SourcePackages/checkouts/AckGen" DIR=.. diff --git a/Tests/AckGenTests/IntegrationTests.swift b/Tests/AckGenTests/IntegrationTests.swift index dce4581..8ef8ced 100644 --- a/Tests/AckGenTests/IntegrationTests.swift +++ b/Tests/AckGenTests/IntegrationTests.swift @@ -43,7 +43,13 @@ final class IntegrationTests: XCTestCase { // When & Then: Path calculation should correctly extract base directory for testCase in testCases { - let calculatedBase = testCase.input.components(separatedBy: "/Build/")[0] + // Use the improved path calculation that handles edge cases + let calculatedBase: String + if let range = testCase.input.range(of: "/Build/", options: .backwards) { + calculatedBase = String(testCase.input[.. Date: Sat, 7 Feb 2026 01:17:32 +0000 Subject: [PATCH 5/6] Refactor tests to use helper function and ensure consistent path logic Co-authored-by: MartinP7r <2669027+MartinP7r@users.noreply.github.com> --- Tests/AckGenTests/IntegrationTests.swift | 55 +++++++++--------------- diagnose_path.swift | 2 +- 2 files changed, 21 insertions(+), 36 deletions(-) diff --git a/Tests/AckGenTests/IntegrationTests.swift b/Tests/AckGenTests/IntegrationTests.swift index 8ef8ced..ba0cbfb 100644 --- a/Tests/AckGenTests/IntegrationTests.swift +++ b/Tests/AckGenTests/IntegrationTests.swift @@ -20,6 +20,17 @@ final class IntegrationTests: XCTestCase { super.tearDown() } + /// Helper function to calculate base path from PROJECT_TEMP_DIR using the production logic + /// This matches the logic in Sources/AckGenCLI/AckGen.swift + private func calculateBasePath(from projectTempDir: String) -> String { + if let range = projectTempDir.range(of: "/Build/", options: .backwards) { + return String(projectTempDir[.. Date: Sat, 7 Feb 2026 01:19:04 +0000 Subject: [PATCH 6/6] Add workflow permissions for security compliance Co-authored-by: MartinP7r <2669027+MartinP7r@users.noreply.github.com> --- .github/workflows/integration-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 8f07c96..49c2383 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -14,6 +14,8 @@ jobs: integration-test: name: Integration Tests runs-on: macos-latest + permissions: + contents: read steps: - name: Checkout