From 6ce64aea2ae0bacd6dabd76ae1405762ff4ee502 Mon Sep 17 00:00:00 2001 From: Lucas BIANCIOTTO Date: Thu, 26 Feb 2026 10:33:03 +0100 Subject: [PATCH 1/6] Update package Swift: 6.2 ViewInspector: 0.10.3 Netectera SDK: 2.6.00 --- .gitignore | 1 + Example/Example.entitlements | 2 +- Example/Example.xcodeproj/project.pbxproj | 7 +++++++ .../xcshareddata/swiftpm/Package.resolved | 6 +++--- Package.resolved | 10 +++++----- Package.swift | 6 +++--- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index f96f838..b8b1092 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,4 @@ sonarqube-generic-coverage.xml Example/Config/Secrets.xcconfig Sources/Monext/AppMetadata.plist Example/fastlane/report.xml +/dist diff --git a/Example/Example.entitlements b/Example/Example.entitlements index 2eb6d71..938e10d 100644 --- a/Example/Example.entitlements +++ b/Example/Example.entitlements @@ -4,7 +4,7 @@ com.apple.developer.in-app-payments - merchant.homo.sdk.monext.com + merchant.com.payline.prod diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 1d740c5..1689921 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 5ED276AF2F2A6B90007E91D0 /* Monext in Frameworks */ = {isa = PBXBuildFile; productRef = 5ED276AE2F2A6B90007E91D0 /* Monext */; }; E075B1FD2E0414C400417D6F /* Monext in Frameworks */ = {isa = PBXBuildFile; productRef = E075B1FC2E0414C400417D6F /* Monext */; }; E0A0F2222E69D84600658ED3 /* Monext in Embed Frameworks */ = {isa = PBXBuildFile; productRef = E075B1FC2E0414C400417D6F /* Monext */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; /* End PBXBuildFile section */ @@ -88,6 +89,7 @@ buildActionMask = 2147483647; files = ( E075B1FD2E0414C400417D6F /* Monext in Frameworks */, + 5ED276AF2F2A6B90007E91D0 /* Monext in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -161,6 +163,7 @@ name = Example; packageProductDependencies = ( E075B1FC2E0414C400417D6F /* Monext */, + 5ED276AE2F2A6B90007E91D0 /* Monext */, ); productName = Example; productReference = D99B79E32CA4E2B30010B87C /* Example.app */; @@ -625,6 +628,10 @@ /* End XCLocalSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 5ED276AE2F2A6B90007E91D0 /* Monext */ = { + isa = XCSwiftPackageProductDependency; + productName = Monext; + }; E075B1FC2E0414C400417D6F /* Monext */ = { isa = XCSwiftPackageProductDependency; productName = Monext; diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4c16703..db9651a 100644 --- a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "d0f40a25e31746a39b3945353beba95b68b3e502a59f689c285cf8b5267b4dc1", + "originHash" : "dab88b11e72284b325cdcf8c9f497c4292569dbc29cf275e4d9e674e51887ddd", "pins" : [ { "identity" : "spm", "kind" : "remoteSourceControl", "location" : "https://github.com/ios-3ds-sdk/SPM.git", "state" : { - "revision" : "421449969baa6ef80c33fd077c217f724ccfd348", - "version" : "2.5.30" + "revision" : "53d5820988951162d9172c3ea7b2ea4bac4447b9", + "version" : "2.6.0" } } ], diff --git a/Package.resolved b/Package.resolved index a4a5314..1bc7819 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "8629114665fa2f623453a96b1eedfb885f805145a1b23a20941a8a2f06d2ba2e", + "originHash" : "9d07c511bb3d7ef2a36dfa9b6412b086a242792841ed1fdb780ccef6551b38f1", "pins" : [ { "identity" : "spm", "kind" : "remoteSourceControl", "location" : "https://github.com/ios-3ds-sdk/SPM.git", "state" : { - "revision" : "421449969baa6ef80c33fd077c217f724ccfd348", - "version" : "2.5.30" + "revision" : "53d5820988951162d9172c3ea7b2ea4bac4447b9", + "version" : "2.6.0" } }, { @@ -15,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/nalexn/ViewInspector.git", "state" : { - "revision" : "a6fcac8485bc8f57b2d2b55bb6d97138e8659e4b", - "version" : "0.10.2" + "revision" : "e9a06346499a3a889165647e3f23f8a7b2609a1c", + "version" : "0.10.3" } } ], diff --git a/Package.swift b/Package.swift index d11799d..98af86d 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.0 +// swift-tools-version: 6.2 import PackageDescription @@ -14,8 +14,8 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/nalexn/ViewInspector.git", from: "0.9.0"), - .package(url: "https://github.com/ios-3ds-sdk/SPM.git", exact: "2.5.30") + .package(url: "https://github.com/nalexn/ViewInspector.git", from: "0.10.3"), + .package(url: "https://github.com/ios-3ds-sdk/SPM.git", exact: "2.6.00") ], targets: [ .target( From 5072bc30dc07704e8e60d8c2c8c54a81e8d3c70d Mon Sep 17 00:00:00 2001 From: Lucas BIANCIOTTO Date: Thu, 26 Feb 2026 10:35:17 +0100 Subject: [PATCH 2/6] Remove the 'v' from the tag --- .github/workflows/build-and-release.yml | 2 +- .github/workflows/test-and-sonar.yml | 5 +++++ Tests/MonextTests/ThreeDS/Mock/MockThreeDS2Service.swift | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index b7938ee..4dcc792 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -178,7 +178,7 @@ jobs: - name: Create Release and Upload Assets uses: softprops/action-gh-release@v2 with: - tag_name: v${{ needs.build.outputs.version }} + tag_name: ${{ needs.build.outputs.version }} name: 🚀 Monext v${{ needs.build.outputs.version }} body_path: release-notes.md draft: false diff --git a/.github/workflows/test-and-sonar.yml b/.github/workflows/test-and-sonar.yml index 4d8139a..3b39eda 100644 --- a/.github/workflows/test-and-sonar.yml +++ b/.github/workflows/test-and-sonar.yml @@ -23,6 +23,11 @@ jobs: run: | mkdir -p ${{ github.workspace }}/../ ln -s ${{ github.workspace }} ${{ github.workspace }}/../Monext + + - name: Setup Swift 6.2.0 + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '26.2' - name: Install xcresultparser run: | diff --git a/Tests/MonextTests/ThreeDS/Mock/MockThreeDS2Service.swift b/Tests/MonextTests/ThreeDS/Mock/MockThreeDS2Service.swift index 4a48495..fa3986a 100644 --- a/Tests/MonextTests/ThreeDS/Mock/MockThreeDS2Service.swift +++ b/Tests/MonextTests/ThreeDS/Mock/MockThreeDS2Service.swift @@ -130,11 +130,14 @@ class MockThreeDS2Service: NSObject, ThreeDS2Service, @unchecked Sendable { if let tx = transactionToReturn { return tx } else { - // Avoid constructing SDK Transaction types that may be unavailable in test target. throw ThreeDS2Error.SDKRuntime(message: "No transaction configured on MockThreeDS2Service", errorCode: nil, cause: nil) } } + func createTransaction(directoryServerId: String, messageVersion: String?, additionalParameters: [String: String]?) throws -> any ThreeDS_SDK.Transaction { + return try createTransaction(directoryServerId: directoryServerId, messageVersion: messageVersion) + } + func getWarnings() throws -> [ThreeDS_SDK.Warning] { guard didInitialize else { throw ThreeDS2Error.SDKNotInitialized(message: "SDK not initialized", cause: nil) From 1a29f84febaa4de04346a5470a8a88961765bded Mon Sep 17 00:00:00 2001 From: Lucas BIANCIOTTO Date: Thu, 26 Feb 2026 11:21:05 +0100 Subject: [PATCH 3/6] Update 1.0.6 --- Sources/Monext/AppMetadata.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Monext/AppMetadata.plist b/Sources/Monext/AppMetadata.plist index 30b4e4f..e22034d 100644 --- a/Sources/Monext/AppMetadata.plist +++ b/Sources/Monext/AppMetadata.plist @@ -5,7 +5,7 @@ NetceteraAPIKey ${NETCETERA_API_KEY} CFBundleShortVersionString - 1.0.5 + 1.0.6 CFBundleVersion From cdbfa7c6973255f11d2f3f277b91a048d4a21e16 Mon Sep 17 00:00:00 2001 From: Lucas BIANCIOTTO Date: Thu, 26 Feb 2026 11:36:22 +0100 Subject: [PATCH 4/6] Fix build & release pipelines --- .github/workflows/build-and-release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 4dcc792..520eafb 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -6,7 +6,7 @@ on: jobs: build: - runs-on: macos-14 + runs-on: macos-15 outputs: version: ${{ steps.get-version.outputs.version }} should-release: ${{ steps.check-release.outputs.should-release }} @@ -17,10 +17,10 @@ jobs: with: fetch-depth: 0 - - name: Set up Xcode 16.2 + - name: Set up Xcode 26.2 uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '16.2' + xcode-version: '26.2' - name: Download iOS and iOS Simulator Platforms run: | @@ -119,7 +119,7 @@ jobs: release: needs: build - runs-on: macos-latest + runs-on: macos-15 if: needs.build.outputs.should-release == 'true' steps: From 71c548d8091ddd6bc3791e57090293922b089ebb Mon Sep 17 00:00:00 2001 From: Lucas BIANCIOTTO Date: Thu, 26 Feb 2026 16:19:57 +0100 Subject: [PATCH 5/6] Fix build & type --- .../ThreeDS/Models/ChallengeStatus.swift | 2 +- build_xcframework.sh | 32 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Sources/Monext/ThreeDS/Models/ChallengeStatus.swift b/Sources/Monext/ThreeDS/Models/ChallengeStatus.swift index 6d990ef..1354adb 100644 --- a/Sources/Monext/ThreeDS/Models/ChallengeStatus.swift +++ b/Sources/Monext/ThreeDS/Models/ChallengeStatus.swift @@ -8,7 +8,7 @@ import Foundation @preconcurrency import ThreeDS_SDK -public enum ChallengeStatus: Sendable { +public enum ChallengeStatus: @unchecked Sendable { case completed(ThreeDS_SDK.CompletionEvent) case cancelled case timedout diff --git a/build_xcframework.sh b/build_xcframework.sh index 505b5f6..ea5a273 100755 --- a/build_xcframework.sh +++ b/build_xcframework.sh @@ -59,11 +59,41 @@ build_framework() { # Copy Bundle bundle_dir="$product_path/${PACKAGE}_$scheme.bundle" if [ -d "$bundle_dir" ]; then - cp -prv "$bundle_dir"/* "$framework_path/" || exit 17 + cp -prv "$bundle_dir" "$framework_path/" || exit 17 else echo "BUNDLE NOT FOUND!!!" read -n 1 -p "Wait:" mainmenuinput fi + + # Create Info.plist + VERSION=$(plutil -extract CFBundleShortVersionString raw Sources/Monext/AppMetadata.plist 2>/dev/null || echo "1.0.0") + cat > "$framework_path/Info.plist" << EOF + + + + + CFBundleExecutable + $scheme + CFBundleIdentifier + com.monext.sdk + CFBundleName + $scheme + CFBundlePackageType + FMWK + CFBundleShortVersionString + $VERSION + CFBundleVersion + 1 + MinimumOSVersion + 16.0 + CFBundleSupportedPlatforms + + iPhoneOS + + + +EOF + echo "✅ Info.plist créé pour $scheme ($sdk)" } create_xcframework() { From b33505da412088896f5af5f54927665fff713561 Mon Sep 17 00:00:00 2001 From: Lucas BIANCIOTTO Date: Thu, 26 Feb 2026 16:58:32 +0100 Subject: [PATCH 6/6] Add Netcetera key in package --- .env.local.example | 1 + .github/workflows/build-and-release.yml | 135 +++++------------- .github/workflows/test-and-sonar.yml | 37 +++-- .gitignore | 2 + .../xcshareddata/xcschemes/Monext.xcscheme | 2 +- Package.resolved | 2 +- Package.swift | 38 +++-- Plugins/InjectSecrets/plugin.swift | 23 +++ Sources/InjectSecretsExecutable/main.swift | 94 ++++++++++++ Sources/Monext/AppMetadata.plist | 2 - .../Monext/Configuration/MonextConfig.swift | 13 +- build_xcframework.sh | 28 +++- 12 files changed, 248 insertions(+), 129 deletions(-) create mode 100644 .env.local.example create mode 100644 Plugins/InjectSecrets/plugin.swift create mode 100644 Sources/InjectSecretsExecutable/main.swift diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 0000000..f987bc5 --- /dev/null +++ b/.env.local.example @@ -0,0 +1 @@ +NETCETERA_API_KEY=ta_clé_ici diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 520eafb..df2530b 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -38,12 +38,12 @@ jobs: IPHONESIMULATOR_SDK=$(xcrun --sdk iphonesimulator --show-sdk-path) echo "Device SDK: $IPHONEOS_SDK" echo "Simulator SDK: $IPHONESIMULATOR_SDK" - - if [ ! -d "$IPHONEOS_SDK" ]; then + + if [ ! -d "$IPHONEOS_SDK" ]; then echo "❌ iOS SDK not found at $IPHONEOS_SDK" exit 1 fi - + if [ ! -d "$IPHONESIMULATOR_SDK" ]; then echo "❌ iOS Simulator SDK not found at $IPHONESIMULATOR_SDK" exit 1 @@ -52,7 +52,7 @@ jobs: - name: Get version from plist id: get-version run: | - VERSION=$(plutil -extract CFBundleShortVersionString raw Sources/Monext/AppMetadata.plist) + VERSION=$(plutil -extract CFBundleShortVersionString raw Sources/Monext/Resources/AppMetadata.plist) echo "version=$VERSION" >> $GITHUB_OUTPUT echo "📦 Version détectée: $VERSION" @@ -60,10 +60,8 @@ jobs: id: check-release run: | VERSION="${{ steps.get-version.outputs.version }}" - - # Vérifier si on est sur master/main + if [[ "${{ github.ref }}" == "refs/heads/master" ]] || [[ "${{ github.ref }}" == "refs/heads/main" ]]; then - # Vérifier si le tag existe déjà if git rev-parse "v$VERSION" >/dev/null 2>&1; then echo "should-release=false" >> $GITHUB_OUTPUT echo "🏷️ Tag v$VERSION existe déjà, pas de release" @@ -72,22 +70,10 @@ jobs: echo "✨ Nouvelle version détectée: v$VERSION" fi else - # Pas sur master/main (PR ou autre branche) echo "should-release=false" >> $GITHUB_OUTPUT echo "🔧 Branche: ${{ github.ref_name }}, pas de release" fi - - name: Configure API Key - env: - NETCETERA_API_KEY: ${{ secrets.NETCETERA_API_KEY }} - run: | - if [ -z "$NETCETERA_API_KEY" ]; then - echo "⚠️ NETCETERA_API_KEY secret non configuré" - exit 1 - fi - sed -i '' "s/\${NETCETERA_API_KEY}/$NETCETERA_API_KEY/g" Sources/Monext/AppMetadata.plist - echo "🔑 Clé API configurée" - - name: Clean build folders run: | rm -rf .build build dist Package.resolved .swiftpm/xcode @@ -98,7 +84,13 @@ jobs: swift package show-dependencies - name: Build XCFramework + env: + NETCETERA_API_KEY: ${{ secrets.NETCETERA_API_KEY }} run: | + if [ -z "$NETCETERA_API_KEY" ]; then + echo "⚠️ NETCETERA_API_KEY secret non configuré" + exit 1 + fi chmod +x build_xcframework.sh bash -x build_xcframework.sh @@ -146,30 +138,30 @@ jobs: fi cat > release-notes.md << EOF ## 🚀 Monext v$VERSION - + ### 📋 Changements $COMMITS - + ### 🔧 Installation Monext iOS SDK is available via [Swift Package Manager](https://www.swift.org/documentation/package-manager/). - + ### Swift Package Manager - - 1. **Add the Package Dependency:** + + 1. **Add the Package Dependency:** Follow Apple's guide on [Adding Package Dependencies to Your App](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app). - + 2. **Enter the Repository URL:** \`\`\` https://github.com/Monext/monext-ios-sdk - \`\`\` - - 3. **Specify the Version:** + \`\`\` + + 3. **Specify the Version:** Use version \`$VERSION\` or later. - + ### 📦 Contenu de la release - Monext.xcframework - Archive zip - + --- **Build Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC') **Commit:** \`${{ github.sha }}\` @@ -204,8 +196,7 @@ jobs: RELEASE_RESULT="${{ needs.release.result }}" BRANCH="${{ github.ref_name }}" SHOULD_RELEASE="${{ needs.build.outputs.should-release }}" - - # Déterminer le statut global + if [[ "$BUILD_RESULT" == "failure" ]]; then OVERALL_STATUS="failure" NOTIFICATION_TYPE="Build" @@ -222,13 +213,12 @@ jobs: OVERALL_STATUS="unknown" NOTIFICATION_TYPE="Build" fi - - # Configuration selon le statut + if [[ "$OVERALL_STATUS" == "success" ]]; then ICON_COLOR="Good" ICON="CheckmarkStarburst" STATUS_TEXT="✅ Success" - + if [[ "$RELEASE_RESULT" == "success" ]]; then EXTRA_MESSAGE="Publication sur Github Releases réussie." RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/v${VERSION}" @@ -237,12 +227,9 @@ jobs: \"title\": \"🚀 Voir la release\", \"url\": \"${RELEASE_URL}\" }," - echo "✅ Release v${VERSION} créée avec succès!" - echo "🔗 ${RELEASE_URL}" else EXTRA_MESSAGE="Build réussi sur la branche ${BRANCH} (pas de release créée)." RELEASE_ACTION="" - echo "✅ Build v${VERSION} réussi sur ${BRANCH} (should-release: ${SHOULD_RELEASE})" fi elif [[ "$OVERALL_STATUS" == "failure" ]]; then ICON_COLOR="Attention" @@ -250,19 +237,14 @@ jobs: STATUS_TEXT="❌ Échec" EXTRA_MESSAGE="Échec lors du processus de ${NOTIFICATION_TYPE}." RELEASE_ACTION="" - - echo "❌ Échec du ${NOTIFICATION_TYPE}" else ICON_COLOR="Warning" ICON="Warning" STATUS_TEXT="⚠️ Statut inconnu" EXTRA_MESSAGE="Le statut du workflow est indéterminé." RELEASE_ACTION="" - - echo "⚠️ Statut inconnu" fi - - # Envoi de la notification Teams + curl -X POST "${TEAMS_WEBHOOK_URL}" \ -H "Content-Type: application/json" \ -d @- << EOF @@ -283,73 +265,31 @@ jobs: { "type": "Column", "width": "auto", - "height": "stretch", - "items": [ - { - "type": "Icon", - "name": "${ICON}", - "style": "Filled", - "color": "${ICON_COLOR}" - } - ], + "items": [{ "type": "Icon", "name": "${ICON}", "style": "Filled", "color": "${ICON_COLOR}" }], "spacing": "None" }, { "type": "Column", "width": "stretch", - "items": [ - { - "type": "TextBlock", - "text": "Publication **SDK iOS** - ${STATUS_TEXT}", - "wrap": true, - "size": "Large", - "weight": "Bolder" - } - ], + "items": [{ "type": "TextBlock", "text": "Publication **SDK iOS** - ${STATUS_TEXT}", "wrap": true, "size": "Large", "weight": "Bolder" }], "spacing": "Small" } ], "spacing": "None" }, - { - "type": "TextBlock", - "text": "_version ${VERSION}_", - "wrap": true, - "isSubtle": true, - "spacing": "Small" - }, + { "type": "TextBlock", "text": "_version ${VERSION}_", "wrap": true, "isSubtle": true, "spacing": "Small" }, { "type": "FactSet", "facts": [ - { - "title": "Repository:", - "value": "${{ github.repository }}" - }, - { - "title": "Branch/Tag:", - "value": "${BRANCH}" - }, - { - "title": "Auteur:", - "value": "${{ github.actor }}" - }, - { - "title": "Build Status:", - "value": "${BUILD_RESULT}" - }, - { - "title": "Release Status:", - "value": "${RELEASE_RESULT}" - } + { "title": "Repository:", "value": "${{ github.repository }}" }, + { "title": "Branch/Tag:", "value": "${BRANCH}" }, + { "title": "Auteur:", "value": "${{ github.actor }}" }, + { "title": "Build Status:", "value": "${BUILD_RESULT}" }, + { "title": "Release Status:", "value": "${RELEASE_RESULT}" } ], "separator": true }, - { - "type": "TextBlock", - "text": "${EXTRA_MESSAGE}", - "wrap": true, - "separator": true - } + { "type": "TextBlock", "text": "${EXTRA_MESSAGE}", "wrap": true, "separator": true } ], "actions": [ ${RELEASE_ACTION} @@ -364,10 +304,7 @@ jobs: ] } EOF - - echo "✅ Notification ${NOTIFICATION_TYPE} (${OVERALL_STATUS}) envoyée avec succès" - - # Faire échouer le step si le statut global est en échec + if [[ "$OVERALL_STATUS" == "failure" ]]; then exit 1 fi diff --git a/.github/workflows/test-and-sonar.yml b/.github/workflows/test-and-sonar.yml index 3b39eda..f28099d 100644 --- a/.github/workflows/test-and-sonar.yml +++ b/.github/workflows/test-and-sonar.yml @@ -21,29 +21,50 @@ jobs: - name: Create symlink for compatibility run: | - mkdir -p ${{ github.workspace }}/../ - ln -s ${{ github.workspace }} ${{ github.workspace }}/../Monext - - - name: Setup Swift 6.2.0 + mkdir -p ${{ github.workspace }}/../ + ln -s ${{ github.workspace }} ${{ github.workspace }}/../Monext + + - name: Setup Xcode 26.2 uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '26.2' - + - name: Install xcresultparser run: | brew install xcresultparser + - name: Build plugin executable for macOS host + run: | + xcodebuild build \ + -scheme InjectSecretsExecutable \ + -configuration Debug \ + -destination "platform=macOS,arch=arm64" \ + -derivedDataPath ~/SharedDerivedData + - name: Install SonarQube Scanner run: | wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-6.2.1.4610.zip unzip sonar-scanner-cli-6.2.1.4610.zip echo "${PWD}/sonar-scanner-6.2.1.4610/bin" >> $GITHUB_PATH - + - name: Execute Coverage + env: + NETCETERA_API_KEY: "ta_clé_ici" run: | set -o pipefail - xcodebuild test -sdk ${{ matrix.sdk }} -scheme "Monext" -destination '${{ matrix.destination }}' -enableCodeCoverage YES -resultBundlePath sdk.xcresult -quiet - xcodebuild test -project ./Example/Example.xcodeproj -scheme Example -destination '${{ matrix.destination }}' -enableCodeCoverage YES -resultBundlePath ./example.xcresult + xcodebuild test \ + -sdk ${{ matrix.sdk }} \ + -scheme "Monext" \ + -destination '${{ matrix.destination }}' \ + -enableCodeCoverage YES \ + -resultBundlePath sdk.xcresult \ + -derivedDataPath ~/SharedDerivedData + xcodebuild test \ + -project ./Example/Example.xcodeproj \ + -scheme Example \ + -destination '${{ matrix.destination }}' \ + -enableCodeCoverage YES \ + -resultBundlePath ./example.xcresult xcrun xcresulttool merge sdk.xcresult example.xcresult --output-path=results.xcresult xcresultparser -o xml --coverage results.xcresult > sonarqube-generic-coverage.xml diff --git a/.gitignore b/.gitignore index b8b1092..3ff8de2 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,5 @@ Example/Config/Secrets.xcconfig Sources/Monext/AppMetadata.plist Example/fastlane/report.xml /dist +.env.local +Sources/Monext/APIConfig.swift diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Monext.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Monext.xcscheme index 21c8b6f..2345c8f 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Monext.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Monext.xcscheme @@ -81,4 +81,4 @@ buildConfiguration = "Release" revealArchiveInOrganizer = "YES"> - + \ No newline at end of file diff --git a/Package.resolved b/Package.resolved index 1bc7819..d846d32 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "9d07c511bb3d7ef2a36dfa9b6412b086a242792841ed1fdb780ccef6551b38f1", + "originHash" : "6b0be16ab9fa84b396d800d553c96b1c7e68d119c3d610ee552d5cf43374d0ce", "pins" : [ { "identity" : "spm", diff --git a/Package.swift b/Package.swift index 98af86d..28f11ab 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,10 @@ import PackageDescription let package = Package( name: "Monext", defaultLocalization: "en", - platforms: [.iOS(.v16)], + platforms: [ + .iOS(.v16), + .macOS(.v13) + ], products: [ .library( name: "Monext", @@ -18,18 +21,37 @@ let package = Package( .package(url: "https://github.com/ios-3ds-sdk/SPM.git", exact: "2.6.00") ], targets: [ + + // MARK: - Executable du plugin + .executableTarget( + name: "InjectSecretsExecutable" + ), + + // MARK: - Build plugin + .plugin( + name: "InjectSecrets", + capability: .buildTool(), + dependencies: ["InjectSecretsExecutable"] + ), + + // MARK: - SDK principal .target( name: "Monext", dependencies: [ .product(name: "ThreeDS_SDK", package: "SPM"), ], - path: "Sources", + path: "Sources/Monext", resources: [ - .process("Monext/AppMetadata.plist"), - .process("Monext/Resources/Images.xcassets"), - .process("Monext/Resources/Localizable.xcstrings") - ] + .process("AppMetadata.plist"), + .process("Resources/Images.xcassets"), + .process("Resources/Localizable.xcstrings") + ], + plugins: [ + "InjectSecrets" + ] ), + + // MARK: - Tests .testTarget( name: "MonextTests", dependencies: [ @@ -37,8 +59,8 @@ let package = Package( .product(name: "ViewInspector", package: "ViewInspector") ], resources: [ - .process("API/TestResources") - ] + .process("API/TestResources") + ] ), ] ) diff --git a/Plugins/InjectSecrets/plugin.swift b/Plugins/InjectSecrets/plugin.swift new file mode 100644 index 0000000..c747503 --- /dev/null +++ b/Plugins/InjectSecrets/plugin.swift @@ -0,0 +1,23 @@ +// +// plugin.swift +// Monext +// +// Created by lucas bianciotto on 26/02/2026. +// + +import PackagePlugin +import Foundation + +@main +struct InjectSecretsPlugin: BuildToolPlugin { + func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { + let outputFileURL = context.pluginWorkDirectoryURL.appending(path: "APIConfig.swift") + + return [.buildCommand( + displayName: "Inject API Secrets", + executable: try context.tool(named: "InjectSecretsExecutable").url, + arguments: [outputFileURL.path(percentEncoded: false)], + outputFiles: [outputFileURL] + )] + } +} diff --git a/Sources/InjectSecretsExecutable/main.swift b/Sources/InjectSecretsExecutable/main.swift new file mode 100644 index 0000000..24ab39a --- /dev/null +++ b/Sources/InjectSecretsExecutable/main.swift @@ -0,0 +1,94 @@ +// +// main.swift +// Monext +// +// Created by lucas bianciotto on 26/02/2026. +// + +import Foundation + +// MARK: - Helpers + +func readEnvFile(at path: String) -> [String: String] { + guard let content = try? String(contentsOfFile: path, encoding: .utf8) else { + return [:] + } + + var result: [String: String] = [:] + for line in content.components(separatedBy: .newlines) { + let trimmed = line.trimmingCharacters(in: .whitespaces) + // Ignorer les commentaires et les lignes vides + guard !trimmed.isEmpty, !trimmed.hasPrefix("#") else { continue } + let parts = trimmed.components(separatedBy: "=") + guard parts.count >= 2 else { continue } + let key = parts[0].trimmingCharacters(in: .whitespaces) + let value = parts.dropFirst().joined(separator: "=").trimmingCharacters(in: .whitespaces) + result[key] = value + } + return result +} + +func resolveKey(named name: String) -> String { + // 1. Variables d'environnement (CI / GitHub Actions) + if let value = ProcessInfo.processInfo.environment[name], !value.isEmpty { + fputs("✅ \(name) found in environment variables\n", stderr) + return value + } + + // 2. Fichier .env.local (développement local) + let workDir = FileManager.default.currentDirectoryPath + let envLocalPath = URL(fileURLWithPath: workDir).appendingPathComponent(".env.local").path + let envVars = readEnvFile(at: envLocalPath) + + if let value = envVars[name], !value.isEmpty { + fputs("✅ \(name) found in .env.local\n", stderr) + return value + } + + // 3. Clé vide avec warning + fputs("⚠️ Warning: \(name) not found. Set it in .env.local or as environment variable.\n", stderr) + fputs(" → Copy .env.local.example to .env.local and fill in your values.\n", stderr) + return "" +} + +func toByteArray(_ string: String) -> String { + return string.utf8 + .map { String(format: "0x%02x", $0) } + .joined(separator: ", ") +} + +// MARK: - Main + +guard CommandLine.arguments.count > 1 else { + fputs("❌ Usage: InjectSecretsExecutable \n", stderr) + exit(1) +} + +let outputPath = CommandLine.arguments[1] + +let netecteraAPIKey = resolveKey(named: "NETCETERA_API_KEY") +let netecteraBytes = toByteArray(netecteraAPIKey) + +let content = """ +// ⚠️ Auto-generated by InjectSecretsPlugin - do not edit manually +// This file is gitignored and regenerated at each build + +internal enum APIConfig { + + /// Netcetera 3DS SDK API Key + /// Stored as bytes to avoid plain text exposure in the binary + static var netecteraAPIKey: String { + let bytes: [UInt8] = [\(netecteraBytes)] + return String(bytes: bytes, encoding: .utf8) ?? "" + } + +} +""" + +do { + try content.write(toFile: outputPath, atomically: true, encoding: .utf8) + fputs("✅ APIConfig.swift generated at \(outputPath)\n", stderr) +} catch { + fputs("❌ Failed to write APIConfig.swift: \(error)\n", stderr) + exit(1) +} diff --git a/Sources/Monext/AppMetadata.plist b/Sources/Monext/AppMetadata.plist index e22034d..9a368d1 100644 --- a/Sources/Monext/AppMetadata.plist +++ b/Sources/Monext/AppMetadata.plist @@ -2,8 +2,6 @@ - NetceteraAPIKey - ${NETCETERA_API_KEY} CFBundleShortVersionString 1.0.6 CFBundleVersion diff --git a/Sources/Monext/Configuration/MonextConfig.swift b/Sources/Monext/Configuration/MonextConfig.swift index 7a06c5d..2497acf 100644 --- a/Sources/Monext/Configuration/MonextConfig.swift +++ b/Sources/Monext/Configuration/MonextConfig.swift @@ -19,10 +19,15 @@ public struct MonextConfig { return dict } - // MARK: - Monext Configuration - public static var NetecteraAPIKey: String { - loadPlist()?["NetceteraAPIKey"] as? String ?? "" - } + // MARK: - Sensitive (embarquée dans le binaire via build plugin) + + /// Clé API Netcetera pour le 3DS SDK. + /// Générée à la compilation depuis .env.local ou les variables d'environnement CI. + /// Non visible dans le bundle distribué. + public static var NetecteraAPIKey: String { + APIConfig.netecteraAPIKey + } + } diff --git a/build_xcframework.sh b/build_xcframework.sh index ea5a273..cff1ea2 100755 --- a/build_xcframework.sh +++ b/build_xcframework.sh @@ -29,7 +29,17 @@ build_framework() { echo "Destination: $dest" echo - (xcodebuild -scheme "$scheme" -configuration "$CONFIGURATION" -destination "$dest" -sdk "$sdk" -derivedDataPath "$BUILD_DIR" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_SWIFT_FLAGS="-no-verify-emitted-module-interface") || exit 12 + # La variable NETCETERA_API_KEY est transmise depuis l'environnement CI + # ou depuis .env.local en local — le build plugin InjectSecrets s'en charge + (xcodebuild \ + -scheme "$scheme" \ + -configuration "$CONFIGURATION" \ + -destination "$dest" \ + -sdk "$sdk" \ + -derivedDataPath "$BUILD_DIR" \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + OTHER_SWIFT_FLAGS="-no-verify-emitted-module-interface") || exit 12 product_path="$BUILD_DIR/Build/Products/$CONFIGURATION-$sdk" framework_path="$BUILD_DIR/Build/Products/$CONFIGURATION-$sdk/PackageFrameworks/$scheme.framework" @@ -56,17 +66,16 @@ build_framework() { mkdir "$modules_path/$scheme.swiftmodule" cp -pv "$product_path/$scheme.swiftmodule"/*.* "$modules_path/$scheme.swiftmodule/" || exit 16 - # Copy Bundle + # Copy Bundle (copie le .bundle entier, pas son contenu) bundle_dir="$product_path/${PACKAGE}_$scheme.bundle" if [ -d "$bundle_dir" ]; then cp -prv "$bundle_dir" "$framework_path/" || exit 17 else - echo "BUNDLE NOT FOUND!!!" - read -n 1 -p "Wait:" mainmenuinput + echo "⚠️ BUNDLE NOT FOUND at $bundle_dir" fi # Create Info.plist - VERSION=$(plutil -extract CFBundleShortVersionString raw Sources/Monext/AppMetadata.plist 2>/dev/null || echo "1.0.0") + VERSION=$(plutil -extract CFBundleShortVersionString raw Sources/Monext/Resources/AppMetadata.plist 2>/dev/null || echo "1.0.0") cat > "$framework_path/Info.plist" << EOF @@ -130,8 +139,15 @@ rm -rf "$BUILD_DIR" rm -rf "$DIST_DIR" reset_package_type - set_package_type_as_dynamic "$PACKAGE" + +echo "🔧 Building plugin executable for macOS host..." +xcodebuild build \ + -scheme InjectSecretsExecutable \ + -configuration Release \ + -derivedDataPath "$BUILD_DIR" \ + -destination "platform=macOS" || exit 10 + build_framework "$PACKAGE" "$SIMULATOR_SDK" build_framework "$PACKAGE" "$DEVICE_SDK" create_xcframework "$PACKAGE" "$SIMULATOR_SDK" "$DEVICE_SDK"