├── .github ├── ISSUE_TEMPLATE │ ├── BUG_REPORT.md │ ├── DOCUMENTATION_ISSUE.md │ └── FEATURE_REQUEST.md └── workflows │ ├── ci.yaml │ ├── docs.yaml │ └── release.yaml ├── .gitignore ├── .swift-version ├── .swiftformat ├── .swiftlint.yml ├── CODE_OF_CONDUCT ├── LICENSE ├── NOTICE ├── Package.resolved ├── Package.swift ├── README.md ├── Rakefile ├── Sources ├── Aggregator │ └── main.swift ├── XCRemoteCache │ ├── Artifacts │ │ ├── ArtifactCreator.swift │ │ ├── ArtifactMetaUpdater.swift │ │ ├── ArtifactOrganizer.swift │ │ ├── ArtifactPlugin.swift │ │ ├── ArtifactProcessor.swift │ │ ├── ArtifactSwiftProductsBuilder.swift │ │ ├── FileDependenciesRemapper.swift │ │ ├── SwiftmoduleFileExtension.swift │ │ └── ZipArtifactCreator.swift │ ├── Commands │ │ ├── Libtool │ │ │ ├── FallbackXCLibtoolLogic.swift │ │ │ ├── XCCreateUniversalBinary.swift │ │ │ ├── XCLibtool.swift │ │ │ ├── XCLibtoolLogic.swift │ │ │ └── XCLipo.swift │ │ ├── Plugins │ │ │ ├── PluginError.swift │ │ │ └── Thinning │ │ │ │ ├── ArtifactInspector.swift │ │ │ │ ├── Factories │ │ │ │ ├── ThinningConsumerArtifactsOrganizerFactory.swift │ │ │ │ └── ThinningConsumerSwiftProductsOrganizerFactory.swift │ │ │ │ ├── Parallelization │ │ │ │ └── DispatchGroupParallelizationWorker.swift │ │ │ │ ├── SwiftProductsArchitecturesRecognizer.swift │ │ │ │ ├── SwiftProductsLocationProvider.swift │ │ │ │ ├── ThinningConsumerPostbuildPlugin.swift │ │ │ │ ├── ThinningConsumerPrebuildPlugin.swift │ │ │ │ ├── ThinningCreatorPlugin.swift │ │ │ │ ├── ThinningDiskSwiftcProductsGenerator.swift │ │ │ │ ├── ThinningPlugin.swift │ │ │ │ ├── ThinningPrebuildPlugin.swift │ │ │ │ └── UnzippedArtifactSwiftProductsOrganizer.swift │ │ ├── Postbuild │ │ │ ├── Postbuild.swift │ │ │ ├── PostbuildContext.swift │ │ │ └── XCPostbuild.swift │ │ ├── Prebuild │ │ │ ├── Prebuild.swift │ │ │ ├── PrebuildContext.swift │ │ │ └── XCPrebuild.swift │ │ ├── Prepare │ │ │ ├── CCWrapperBuilder.swift │ │ │ ├── Integrate │ │ │ │ ├── BuildSettingsIntegrateAppender.swift │ │ │ │ ├── IncludeOracle.swift │ │ │ │ ├── Integrate.swift │ │ │ │ ├── IntegrateContext.swift │ │ │ │ ├── LLDBInitMode.swift │ │ │ │ ├── LLDBInitPatcher.swift │ │ │ │ ├── XCIntegrate.swift │ │ │ │ ├── XCRCBinariesPaths.swift │ │ │ │ ├── XcodeProjIntegrate.swift │ │ │ │ └── XcodeSettingsFlags.swift │ │ │ ├── Prepare.swift │ │ │ ├── PrepareContext.swift │ │ │ ├── PrepareMarkContext.swift │ │ │ ├── XCConfig.swift │ │ │ ├── XCPrepare.swift │ │ │ ├── XCPrepareMark.swift │ │ │ ├── XCStats.swift │ │ │ └── XCStatsContext.swift │ │ ├── ProductBinaryCreator │ │ │ └── XCCreateBinary.swift │ │ ├── SwiftFrontend │ │ │ ├── SwiftFrontendArgInput.swift │ │ │ ├── SwiftFrontendContext.swift │ │ │ ├── SwiftFrontendOrchestrator.swift │ │ │ └── XCSwiftFrontend.swift │ │ └── Swiftc │ │ │ ├── FilenameBasedAllowedInputDeterminer.swift │ │ │ ├── MirroredLinkingSwiftcProductsGenerator.swift │ │ │ ├── NoopSwiftcProductsGenerator.swift │ │ │ ├── StaticSwiftcInputReader.swift │ │ │ ├── Swiftc.swift │ │ │ ├── SwiftcContext.swift │ │ │ ├── SwiftcFilemapInputEditor.swift │ │ │ ├── SwiftcOrchestrator.swift │ │ │ ├── SwiftcProductGenerationPlugin.swift │ │ │ ├── SwiftcProductsGenerator.swift │ │ │ └── XCSwiftc.swift │ ├── Config │ │ ├── Mode.swift │ │ └── XCRemoteCacheConfig.swift │ ├── Debugging │ │ └── DSYMOrganizer.swift │ ├── Dependencies │ │ ├── AssetsFileDependenciesReader.swift │ │ ├── CacheModeController.swift │ │ ├── CachedDependenciesWriter.swift │ │ ├── CompilationHistoryOrganizer.swift │ │ ├── Dat │ │ │ └── FileDatWriter.swift │ │ ├── DependenciesMapping.swift │ │ ├── DependenciesReader.swift │ │ ├── DependenciesRemapper.swift │ │ ├── DependenciesWriter.swift │ │ ├── DependencyProcessor.swift │ │ ├── ExceptionsFilteredFileListScanner.swift │ │ ├── FileListScanner.swift │ │ ├── FingerprintOverrideManager.swift │ │ ├── FingerprintSyncer.swift │ │ ├── ListEditor.swift │ │ ├── MarkerReader.swift │ │ ├── MarkerWriter.swift │ │ ├── OverlayDependenciesRemapper.swift │ │ ├── OverlayReader.swift │ │ ├── PathDependenciesRemapperFactory.swift │ │ ├── StaticFileListReader.swift │ │ └── TargetDepdenciesReader.swift │ ├── FileAccess │ │ ├── Copier │ │ │ ├── CopyDiskCopier.swift │ │ │ ├── DiskCopier.swift │ │ │ └── HardLinkDiskCopier.swift │ │ ├── DirAccessor.swift │ │ ├── DirAccessorComposer.swift │ │ ├── FileAccessor.swift │ │ ├── FileManager.swift │ │ ├── LazyFileAccessor.swift │ │ └── SizeProvider.swift │ ├── Fingerprint │ │ ├── EnvironmentFingerprint.swift │ │ ├── FingerprintAccumulator.swift │ │ ├── FingerprintAccumulatorImpl.swift │ │ ├── FingerprintGenerator.swift │ │ └── Hashing │ │ │ └── MD5.swift │ ├── FlowControl │ │ ├── GlobalCacheSwitcher.swift │ │ └── RemoteCommitInfo.swift │ ├── Git │ │ ├── GitClient.swift │ │ └── GitCommitManager.swift │ ├── Logger │ │ └── Logger.swift │ ├── Models │ │ ├── MainArtifactMeta.swift │ │ ├── MetaReader.swift │ │ └── MetaWriter.swift │ ├── Network │ │ ├── Authentication │ │ │ ├── AWSV4Signature.swift │ │ │ ├── AWSV4SigningKey.swift │ │ │ ├── CanonicalRequest.swift │ │ │ ├── HMAC.swift │ │ │ ├── SHA256.swift │ │ │ └── StringToSign.swift │ │ ├── CacheInvalidator.swift │ │ ├── CachedNetworkClient.swift │ │ ├── IgnoringCertificatesTrustManager.swift │ │ ├── LocalURLBuilder.swift │ │ ├── NetworkClient.swift │ │ ├── NetworkClientImpl.swift │ │ ├── NetworkServerProbe.swift │ │ ├── RemoteNetworkClient.swift │ │ ├── RemoteNetworkClientAbstractFactory.swift │ │ ├── ReplicatedRemotesNetworkClient.swift │ │ ├── URLBuilderImpl.swift │ │ └── URLSessionFactory.swift │ ├── Output │ │ ├── FilteredInvocationStorage.swift │ │ ├── InvocationStorage.swift │ │ ├── XCEncoders │ │ │ ├── XCEncoderAbstractFactory.swift │ │ │ ├── XCJSONEncoder.swift │ │ │ └── XCYAMLEncoder.swift │ │ ├── XCOutputFormat.swift │ │ └── XCRemoteCacheEncoder.swift │ ├── Shell │ │ ├── Shell.swift │ │ ├── ShellCommandsProcessor.swift │ │ └── ShellOut.swift │ ├── Stats │ │ ├── CacheHitLogger.swift │ │ ├── Counters.swift │ │ ├── ExclusiveFileAccessor.swift │ │ ├── FileStatistics.swift │ │ ├── StatsCoordinator.swift │ │ └── XCRemoteCacheStatistics.swift │ ├── Utils │ │ ├── Array+Utils.swift │ │ ├── Date+Utils.swift │ │ ├── ENVReader.swift │ │ ├── FileTouch.swift │ │ ├── String+TileInPath.swift │ │ └── URL+ThrowingInitializer.swift │ └── Xcode │ │ ├── BuildActionType.swift │ │ └── XcodeProbe.swift ├── xcld │ ├── XCLDMain.swift │ └── main.swift ├── xcldplusplus │ ├── XCLDPlusPlus.swift │ └── main.swift ├── xclibtool │ ├── XCLibtoolMain.swift │ └── main.swift ├── xclibtoolSupport │ └── XCLibtoolHelper.swift ├── xclipo │ ├── XCLipoMain.swift │ └── main.swift ├── xcpostbuild │ └── main.swift ├── xcprebuild │ └── main.swift ├── xcprepare │ ├── ArgumentsSupport.swift │ ├── XCPrepareMain.swift │ └── main.swift ├── xcswift-frontend │ ├── XCSwiftcFrontendMain.swift │ └── main.swift └── xcswiftc │ ├── XCSwiftcMain.swift │ └── main.swift ├── Tests ├── LinuxMain.swift ├── XCRemoteCacheTests │ ├── Artifacts │ │ ├── ArtifactMetaUpdaterTests.swift │ │ ├── ArtifactSwiftProductsBuilderImplTests.swift │ │ ├── BuildArtifactCreatorTests.swift │ │ ├── TextFileDependenciesRemapperTests.swift │ │ ├── UnzippedArtifactProcessorTests.swift │ │ ├── ZipArtifactCreatorTests.swift │ │ └── ZipArtifactOrganizerTests.swift │ ├── Commands │ │ ├── MirroredLinkingSwiftcProductsGeneratorTests.swift │ │ ├── Plugins │ │ │ └── Thinning │ │ │ │ ├── DefaultArtifactInspectorTests.swift │ │ │ │ ├── DefaultSwiftProductsArchitecturesRecognizerTests.swift │ │ │ │ ├── DefaultSwiftProductsLocationProviderTests.swift │ │ │ │ ├── DispatchGroupParallelizationWorkerTests.swift │ │ │ │ ├── ThinningConsumerPostbuildPluginTests.swift │ │ │ │ ├── ThinningConsumerPrebuildPluginTest.swift │ │ │ │ ├── ThinningCreatorPluginTests.swift │ │ │ │ ├── ThinningDiskSwiftcProductsGeneratorTests.swift │ │ │ │ ├── ThinningPluginTests.swift │ │ │ │ └── UnzippedArtifactSwiftProductsOrganizerTests.swift │ │ ├── PostbuildContextTests.swift │ │ ├── PostbuildTests.swift │ │ ├── PrebuildContextTests.swift │ │ ├── PrebuildTests.swift │ │ ├── Prepare │ │ │ └── Integrate │ │ │ │ ├── FileLLDBInitPatcherTests.swift │ │ │ │ ├── IncludeExcludeOracleTests.swift │ │ │ │ ├── IntegrateContextTests.swift │ │ │ │ ├── XcodeProjBuildSettingsIntegrateAppenderTests.swift │ │ │ │ ├── XcodeSettingsCFlagsTests.swift │ │ │ │ └── XcodeSettingsSwiftFlagsSetterTests.swift │ │ ├── PrepareContextTests.swift │ │ ├── PrepareMarkContextTests.swift │ │ ├── PrepareTests.swift │ │ ├── RemoteCommitInfoTests.swift │ │ ├── SwiftFrontend │ │ │ ├── SwiftFrontendArgInputTests.swift │ │ │ └── SwiftFrontendContextTests.swift │ │ ├── SwiftFrontendOrchestratorTests.swift │ │ ├── SwiftcContextTests.swift │ │ ├── SwiftcFilemapInputEditorTests.swift │ │ ├── SwiftcOrchestratorTests.swift │ │ ├── SwiftcTests.swift │ │ └── XCCCTests.swift │ ├── Config │ │ ├── ModeTests.swift │ │ └── XCRemoteCacheConfigReaderTests.swift │ ├── Debugging │ │ └── DynamicDSYMOrganizerTests.swift │ ├── Dependencies │ │ ├── AssetsFileDependenciesReaderTests.swift │ │ ├── CachedFileDependenciesWriterFactoryTests.swift │ │ ├── Dat │ │ │ └── FileDatWriterTests.swift │ │ ├── DependenciesReaderPerformanceTest.swift │ │ ├── DependenciesReaderTests.swift │ │ ├── DependenciesRemapperCompositeTests.swift │ │ ├── DependenciesWriterTests.swift │ │ ├── DependencyProcessorImplTests.swift │ │ ├── ExceptionsFileListScannerTests.swift │ │ ├── FileFingerprintSyncerTests.swift │ │ ├── FileListEditorTests.swift │ │ ├── FileListScannerImplTests.swift │ │ ├── FileMarkerReaderTests.swift │ │ ├── FingerprintOverrideManagerImplTests.swift │ │ ├── OverlayDependenciesRemapperTests.swift │ │ ├── OverlayReaderTests.swift │ │ ├── PathDependenciesRemapperFactoryTests.swift │ │ ├── PhaseCacheModeControllerTests.swift │ │ ├── StaticFileListReaderTests.swift │ │ ├── StringDependenciesRemapperTests.swift │ │ └── TargetDependenciesReaderTests.swift │ ├── FileAccess │ │ ├── Copier │ │ │ ├── CopyDiskCopierTests.swift │ │ │ └── HardLinkDiskCopierTests.swift │ │ ├── DirScannerTests.swift │ │ ├── DiskUsageSizeProviderTests.swift │ │ ├── FileManagerUtilitiesTests.swift │ │ └── LazyFileAccessorTests.swift │ ├── Fingerprint │ │ ├── EnvironmentFingerprintGeneratorTests.swift │ │ └── Hashing │ │ │ └── MD5Tests.swift │ ├── FlowControl │ │ └── GlobalCacheSwitcherTests.swift │ ├── Git │ │ ├── GitClientErrorTests.swift │ │ └── GitClientImplTests.swift │ ├── Helpers │ │ ├── Atomic.swift │ │ ├── DataHelpers.swift │ │ ├── FileOperations.swift │ │ ├── FileXCTestCase.swift │ │ ├── OptionalHelpers.swift │ │ ├── ProcessHelpers.swift │ │ └── URLStringExpressive.swift │ ├── Models │ │ └── JsonMetaWriterTests.swift │ ├── Network │ │ ├── Authentication │ │ │ ├── AWSV4SignatureTest.swift │ │ │ ├── AWSV4SigningKeyTest.swift │ │ │ ├── CanonicalRequestTest.swift │ │ │ └── StringToSignTest.swift │ │ ├── CacheInvalidatorTests.swift │ │ ├── DefaultURLSessionFactoryTests.swift │ │ ├── LocalURLBuilderImplTests.swift │ │ ├── LowestLatencyNetworkServerProbeTests.swift │ │ ├── NetworkClientImplTests.swift │ │ ├── ReplicatedRemotesNetworkClientTests.swift │ │ └── URLBuilderImplTests.swift │ ├── Output │ │ ├── FilteredInvocationStorageTests.swift │ │ └── InvocationFileStorageTests.swift │ ├── ShellTests.swift │ ├── Stats │ │ ├── ActionSpecificCacheHitLoggerTests.swift │ │ ├── ExclusiveFileTests.swift │ │ └── FileStatsCoordinatorTests.swift │ ├── TestData │ │ └── Dependencies │ │ │ ├── AssetsFileDependenciesReaderTests │ │ │ └── assetcatalog_dependencies_sample │ │ │ ├── DependenciesReaderPerformanceTest │ │ │ └── dependencies.d │ │ │ └── JsonOverlayReaderTests │ │ │ ├── overlayReaderDefault.json │ │ │ └── overlayReaderEmpty.json │ ├── TestDoubles │ │ ├── ActionSwiftcProductGenerationPlugin.swift │ │ ├── ArtifactConsumerPostbuildPluginSpy.swift │ │ ├── ArtifactOrganizerFake.swift │ │ ├── ArtifactSwiftProductsBuilderSpy.swift │ │ ├── CCWrapperBuilderFake.swift │ │ ├── CacheInvalidatorFake.swift │ │ ├── CacheModeControllerFake.swift │ │ ├── CountersFake.swift │ │ ├── DSYMOrganizerFake.swift │ │ ├── DependenciesReaderFake.swift │ │ ├── DependenciesRemapperFake.swift │ │ ├── DependenciesWriterSpy.swift │ │ ├── DestroyerArtifactProcessor.swift │ │ ├── DirAccessorFake.swift │ │ ├── DisallowedExclusiveFileAccessor.swift │ │ ├── DiskArtifactCreator.swift │ │ ├── DiskCopierFake.swift │ │ ├── ExtraArgumentShellCommandsProcessor.swift │ │ ├── ExtraArtifactConsumerPlugin.swift │ │ ├── FakeExclusiveFileAccessor.swift │ │ ├── FileAccessorFake.swift │ │ ├── FileListScannerFake.swift │ │ ├── FileManagerFake.swift │ │ ├── FingerprintAccumulatorFake.swift │ │ ├── GitClientFake.swift │ │ ├── InMemoryGlobalCacheSwitcher.swift │ │ ├── InMemoryInvocationStorage.swift │ │ ├── InMemoryStatsCoordinator.swift │ │ ├── ListReaderFake.swift │ │ ├── MainArtifactSampleMeta.swift │ │ ├── MarkerWriterSpy.swift │ │ ├── MetaAppenderArtifactCreatorPlugin.swift │ │ ├── NetworkClientFake.swift │ │ ├── NoopArtifactProcessor.swift │ │ ├── OverlayReaderFake.swift │ │ ├── PostShellCommandsProcessor.swift │ │ ├── ShellOutSpy.swift │ │ ├── SwiftcInputReaderStub.swift │ │ ├── SwiftcMock.swift │ │ ├── SwiftcProductsGeneratorFake.swift │ │ ├── SwiftcProductsGeneratorSpy.swift │ │ ├── ThinningConsumerArtifactOrganizerFakeFactory.swift │ │ ├── ThinningConsumerArtifactsOrganizerFakeFactory.swift │ │ ├── ThinningConsumerSwiftProductsOrganizerFactoryFake.swift │ │ ├── TimeoutingNetworkClient.swift │ │ ├── TouchSpy.swift │ │ ├── URLBuilderFake.swift │ │ ├── URLProtocolStub.swift │ │ └── WorkerFake.swift │ ├── XCRemoteCacheTests.swift │ ├── XCTestManifests.swift │ └── Xcode │ │ └── XcodeProbeImplTests.swift └── xclibtoolSupportTests │ └── XCLibtoolHelperTests.swift ├── backend-example ├── Dockerfile └── nginx.conf ├── catalog-info.yaml ├── cocoapods-plugin ├── Gemfile ├── README.md ├── Rakefile ├── cocoapods-xcremotecache.gemspec └── lib │ ├── cocoapods-xcremotecache.rb │ ├── cocoapods-xcremotecache │ ├── command.rb │ ├── command │ │ ├── hooks.rb │ │ ├── podfile.rb │ │ └── xcremotecache.rb │ └── gem_version.rb │ └── cocoapods_plugin.rb ├── docs ├── Development.md ├── FAQ.md ├── design │ ├── ArchitecturalDesigns.md │ └── SwiftDriverIntegration.md └── img │ ├── build-phases.png │ ├── build-settings.png │ ├── console.png │ ├── debug-breakpoint.png │ ├── debug-phase-update.png │ ├── debug-scheme-wait.png │ ├── debug-scheme.png │ ├── debug-wait.png │ ├── driver-dark.png │ ├── driver-scenario1-dark.png │ ├── driver-scenario1.png │ ├── driver-scenario2-dark.png │ ├── driver-scenario2.png │ ├── driver.png │ ├── dsym-default.png │ ├── dsym-warning.png │ ├── logo-dark.png │ ├── logo.png │ ├── pre-driver-dark.png │ ├── pre-driver.png │ └── sample-driver-timeline.png ├── e2eTests ├── StandaloneSampleApp │ ├── .rcinfo │ ├── MixedTarget │ │ ├── MixedTarget-Bridging-Header.h │ │ ├── MixedTarget.swift │ │ └── SomeObjC.h │ ├── StandaloneApp.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── SampleWatchApp.xcscheme │ │ │ └── StaticFramework.xcscheme │ ├── StandaloneApp │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── SceneDelegate.swift │ │ ├── StandaloneObjc.h │ │ ├── StandaloneObjc.m │ │ └── ViewController.swift │ ├── StaticFramework │ │ ├── StaticFramework.h │ │ └── StaticFrameworkFile.swift │ └── WatchExtension │ │ ├── Info.plist │ │ ├── WatchExtension.swift │ │ └── WatchExtensionExtension.swift ├── XCRemoteCacheSample │ ├── XCRemoteCacheSample.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── XCRemoteCacheSample │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── SceneDelegate.swift │ │ └── ViewController.swift ├── nginx │ └── nginx.conf └── tests │ ├── README.md │ ├── default.Podfile │ ├── excludeIphone.Podfile │ ├── excludeIphone.Podfile.config │ ├── excludeIphone.Podfile.expectations │ ├── excludeWatch.Podfile │ ├── excludeWatch.Podfile.config │ └── multiplePods.Podfile └── tasks └── e2e.rb /.github/ISSUE_TEMPLATE/DOCUMENTATION_ISSUE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 📕 Documentation Issue 3 | about: Suggestion for a change in a documentation 4 | 5 | --- 6 | 7 | 10 | 11 | **A suggestion** 12 | 13 | 14 | **Which file, section, line** 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🙏 Future Request 3 | about: Suggestion for an improvement, either behaviour or implementation 4 | 5 | --- 6 | 7 | 10 | 11 | 12 | **Expected/desired behavior** 13 | 14 | 15 | **Relevant integration setup** 16 | 17 | [ ] CocoaPods cocoapods-xcremotecache plugin 18 | [ ] Automatic integration using `xcprepare integrate ...` 19 | [ ] Manual integration 20 | [ ] Carthage 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | SwiftLint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: SwiftLint 11 | uses: norio-nomura/action-swiftlint@3.1.0 12 | with: 13 | args: --strict 14 | 15 | macOS: 16 | runs-on: macos-14 17 | env: 18 | XCODE_VERSION: ${{ '14.3.1' }} 19 | steps: 20 | - name: Select Xcode 21 | run: "sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app" 22 | - name: Checkout 23 | uses: actions/checkout@v1 24 | - name: Install nginx 25 | run: brew install nginx 26 | - name: Build and Run 27 | run: rake build[release] 28 | - name: Test 29 | run: rake test 30 | - name: E2ETests 31 | run: rake e2e_only 32 | -------------------------------------------------------------------------------- /.github/workflows/docs.yaml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | docs: 10 | runs-on: macos-14 11 | env: 12 | XCODE_VERSION: ${{ '14.3.1' }} 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Select Xcode 16 | run: "sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app" 17 | - name: "Generate documentation" 18 | run: "swift package --allow-writing-to-directory ./docs generate-documentation --target XCRemoteCache --disable-indexing --transform-for-static-hosting --output-path ./docs --hosting-base-path XCRemoteCache/" 19 | - name: Deploy GH-pages 20 | uses: peaceiris/actions-gh-pages@v3 21 | with: 22 | github_token: ${{ secrets.GITHUB_TOKEN }} 23 | publish_dir: ./docs 24 | keep_files: false 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | *.xcodeproj/ 5 | *.xcworkspace/ 6 | DerivedData 7 | /.swiftpm/ 8 | releases 9 | tmp/ 10 | .idea/ 11 | xcuserdata 12 | *.gem 13 | Pods/ 14 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.1 2 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | # Rule configurations 2 | --allman false 3 | --binarygrouping 4,8 4 | --commas always 5 | --comments indent 6 | --decimalgrouping 3,5 7 | --elseposition same-line 8 | --empty void 9 | --exponentcase lowercase 10 | --exponentgrouping disabled 11 | --fractiongrouping enabled 12 | --header strip 13 | --hexgrouping none 14 | --hexliteralcase lowercase 15 | --ifdef noindent 16 | --indent 4 17 | --indentcase false 18 | --xcodeindentation enabled 19 | --linebreaks lf 20 | --octalgrouping 4,8 21 | --patternlet inline 22 | --self remove 23 | --semicolons inline 24 | --stripunusedargs closure-only 25 | --trimwhitespace always 26 | --wraparguments beforefirst 27 | --wrapcollections beforefirst 28 | --closingparen balanced 29 | --xcodeindentation enabled 30 | 31 | # Disabled rules 32 | --disable numberFormatting 33 | --disable consecutiveBlankLines 34 | --disable andOperator 35 | --disable spaceAroundOperators 36 | --disable redundantReturn 37 | --disable blankLinesAtStartOfScope 38 | 39 | # Enabled rules 40 | 41 | # Tool options 42 | --symlinks ignore 43 | 44 | # Excluded directories 45 | --exclude Carthage,.build,DerivedData 46 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT: -------------------------------------------------------------------------------- 1 | This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code. 2 | 3 | [code-of-conduct]: https://github.com/spotify/code-of-conduct/blob/main/code-of-conduct.md 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Spotify AB 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | XCRemoteCache 2 | Copyright 2021 Spotify AB 3 | -------------------------------------------------------------------------------- /Sources/Aggregator/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | // Aggregator is an empty target to rebuild all executables 21 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Artifacts/SwiftmoduleFileExtension.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | enum SwiftmoduleFileExtensionType { 23 | case required 24 | case optional 25 | } 26 | 27 | // Type of the file that constitutes a full modulemap package 28 | // RawValue corresponds to the file extension 29 | enum SwiftmoduleFileExtension: String { 30 | case swiftmodule 31 | case swiftdoc 32 | case swiftsourceinfo 33 | case swiftinterface 34 | case privateSwiftinterface = "private.swiftinterface" 35 | case abiJson = "abi.json" 36 | } 37 | 38 | extension SwiftmoduleFileExtension { 39 | /// List of all swiftmodule extensions that should be copied to the artifact 40 | static let SwiftmoduleExtensions: [SwiftmoduleFileExtension: SwiftmoduleFileExtensionType] = [ 41 | .swiftmodule: .required, 42 | .swiftdoc: .required, 43 | .swiftsourceinfo: .optional, 44 | .swiftinterface: .optional, 45 | .privateSwiftinterface: .optional, 46 | .abiJson: .optional, 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Libtool/FallbackXCLibtoolLogic.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | class FallbackXCLibtoolLogic: XCLibtoolLogic { 23 | private let fallbackCommand: String 24 | 25 | init(fallbackCommand: String) { 26 | self.fallbackCommand = fallbackCommand 27 | } 28 | 29 | func run() { 30 | let args = ProcessInfo().arguments 31 | let paramList = [fallbackCommand] + args.dropFirst() 32 | let cargs = paramList.map { strdup($0) } + [nil] 33 | execvp(fallbackCommand, cargs) 34 | 35 | exit(1) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Libtool/XCLibtoolLogic.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | /// XCLibtool wrapper logic that executes the libtool logic 21 | protocol XCLibtoolLogic { 22 | /// Executes xclibtool mocked logic or fallbacks to the libtool execution 23 | func run() 24 | } 25 | 26 | extension XCCreateBinary: XCLibtoolLogic {} 27 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Plugins/PluginError.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Errors thrown from Plugins 23 | enum PluginError: Error { 24 | /// The error is severe and the command should fail immediately 25 | case unrecoverableError(Error) 26 | } 27 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Plugins/Thinning/ThinningPlugin.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Shared logic between thinning plugin producers and consumers 23 | enum ThinningPlugin { 24 | /// Prefix of the meta keys that correspond to the Thinning Plugin 25 | static let fileKeyPrefix = "thinning_" 26 | 27 | /// Finds all artifact fileKeys from the thinned artifact meta 28 | /// Returns a dictionary with Product names keys and aritfact fileKey values 29 | static func extractAllProductArtifacts(meta: MainArtifactMeta) -> [String: String] { 30 | let rawKeys = meta.pluginsKeys 31 | 32 | let filteredArtifacts = rawKeys.compactMap { key, value -> (String, String)? in 33 | guard key.hasPrefix(fileKeyPrefix) else { 34 | return nil 35 | } 36 | return (String(key.dropFirst(fileKeyPrefix.count)), value) 37 | } 38 | return Dictionary(uniqueKeysWithValues: filteredArtifacts) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Plugins/Thinning/ThinningPrebuildPlugin.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Abstract class for consumer's consumer and producer plugins 23 | class ThinningConsumerPlugin { 24 | private var wasRun: Bool = false 25 | 26 | deinit { 27 | // initialised but never run plugin suggests that standard target fallbacks to the local development 28 | // and DerivedData still misses build artifacts 29 | guard wasRun else { 30 | let errorMessage = """ 31 | \(type(of: self)) plugin has never been run, thinning cannot be supported. Verify you \ 32 | have active network connection to the remote cache server or fallback to the non-thinned mode. 33 | """ 34 | exit(1, errorMessage) 35 | } 36 | } 37 | 38 | /// called when plugin is run 39 | func onRun() { 40 | wasRun = true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Prepare/Integrate/IncludeOracle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | typealias OracleIdentifierType = String 23 | 24 | /// Controls if the given type should be included or not 25 | /// Example: controls if remote cache integration should be added for a given target or configuration 26 | protocol IncludeOracle { 27 | /// Decides if a given type should be included or not 28 | /// - Parameter identifier: identifier of a type 29 | func shouldInclude(identifier: OracleIdentifierType) -> Bool 30 | } 31 | 32 | struct IncludeExcludeOracle: IncludeOracle { 33 | let excludes: [OracleIdentifierType] 34 | let includes: [OracleIdentifierType] 35 | 36 | 37 | func shouldInclude(identifier: OracleIdentifierType) -> Bool { 38 | // exclude array has precedence 39 | if excludes.contains(identifier) { 40 | return false 41 | } 42 | guard !includes.isEmpty else { 43 | return true 44 | } 45 | return includes.contains(identifier) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Prepare/Integrate/Integrate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Integrates XCRemoteCache into the existing Xcode project 23 | protocol Integrate { 24 | /// Entry point for the XCRemoteCache integration 25 | func run() throws 26 | } 27 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Prepare/Integrate/LLDBInitMode.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | public enum LLDBInitMode: String, Codable, CaseIterable { 21 | /// Do not add anything to .lldbinit (might affect debugging experience) 22 | case none 23 | /// Installs lldb command in a ~/.lldbinit 24 | case user 25 | } 26 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Prepare/Integrate/XCRCBinariesPaths.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Representing locations of all XCRemoteCache binaries (including wrappers and phase scripts) 23 | struct XCRCBinariesPaths { 24 | let prepare: URL 25 | let cc: URL 26 | let swiftc: URL 27 | let libtool: URL 28 | let lipo: URL 29 | let ld: URL 30 | let ldplusplus: URL 31 | let prebuild: URL 32 | let postbuild: URL 33 | } 34 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Prepare/XCConfig.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Print current configuration to the console 23 | public class XCConfig { 24 | private let outputEncoder: XCRemoteCacheEncoder 25 | 26 | public init(format: XCOutputFormat) { 27 | outputEncoder = XCEncoderAbstractFactory().build(for: format) 28 | } 29 | 30 | public func main() { 31 | let env = ProcessInfo.processInfo.environment 32 | let fileManager = FileManager.default 33 | let config: XCRemoteCacheConfig 34 | do { 35 | config = try XCRemoteCacheConfigReader(env: env, fileReader: fileManager).readConfiguration() 36 | } catch { 37 | exit(1, "FATAL: Prepare initialization failed with error: \(error)") 38 | } 39 | 40 | do { 41 | let output = try outputEncoder.encode(config) 42 | print(output) 43 | } catch { 44 | exit(1, "XCInfo failed with error: \(error)") 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Prepare/XCStatsContext.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | 23 | public enum XCStatsContextError: Error { 24 | case invalidAddress(String) 25 | } 26 | 27 | public struct XCStatsContext { 28 | /// Path of the root directory with all statistic files 29 | let statsDir: URL 30 | /// Location of the local cache that stores all fetched artifacts, metas etc 31 | let cacheLocation: URL 32 | } 33 | 34 | extension XCStatsContext { 35 | init(_ config: XCRemoteCacheConfig, fileManager: FileManager) throws { 36 | 37 | statsDir = URL(fileURLWithPath: config.statsDir.expandingTildeInPath) 38 | let cacheURL: URL = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first! 39 | let cacheURLBuilder = LocalURLBuilderImpl(cachePath: cacheURL) 40 | cacheLocation = cacheURLBuilder.localAddress 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Swiftc/FilenameBasedAllowedInputDeterminer.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Decides if an input to the compilation step should allow reusing the cached artifact 23 | protocol AllowedInputDeterminer { 24 | /// Decides if the input file is allowed to be compiled, even not specified in the dependency list 25 | func allowedNonDependencyInput(file: URL) -> Bool 26 | } 27 | 28 | class FilenameBasedAllowedInputDeterminer: AllowedInputDeterminer { 29 | private let filenames: [String] 30 | 31 | init(_ filenames: [String]) { 32 | self.filenames = filenames 33 | } 34 | 35 | func allowedNonDependencyInput(file: URL) -> Bool { 36 | return filenames.contains(file.lastPathComponent) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Swiftc/StaticSwiftcInputReader.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | class StaticSwiftcInputReader: SwiftcInputReader { 23 | private let moduleDependencies: URL? 24 | private let swiftDependencies: URL? 25 | private let compilationFiles: [SwiftFileCompilationInfo] 26 | 27 | init( 28 | moduleDependencies: URL?, 29 | swiftDependencies: URL?, 30 | compilationFiles: [SwiftFileCompilationInfo] 31 | ) { 32 | self.moduleDependencies = moduleDependencies 33 | self.swiftDependencies = swiftDependencies 34 | self.compilationFiles = compilationFiles 35 | } 36 | 37 | func read() throws -> SwiftCompilationInfo { 38 | return .init( 39 | info: .init( 40 | dependencies: moduleDependencies, 41 | swiftDependencies: swiftDependencies 42 | ), 43 | files: compilationFiles 44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Commands/Swiftc/SwiftcProductGenerationPlugin.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Extends the swiftc product generation (when consuming cached artifact(s)) 23 | protocol SwiftcProductGenerationPlugin { 24 | 25 | /// Allows to extend the production generation 26 | /// - Parameter for: info of all compilation files passed to the swiftc invocation 27 | func generate(for: SwiftCompilationInfo) throws 28 | } 29 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Config/Mode.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | public enum Mode: String, Codable, CaseIterable { 21 | case consumer 22 | case producer 23 | case producerFast = "producer-fast" 24 | } 25 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Dependencies/CompilationHistoryOrganizer.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Manages a file that collects all compilation invocations 23 | protocol CompilationHistoryOrganizer { 24 | /// Cleans a state of clang history invocations 25 | func reset() 26 | } 27 | 28 | /// Manages a list of invocations stored in a file 29 | class CompilationHistoryFileOrganizer: CompilationHistoryOrganizer { 30 | private let file: URL 31 | private let fileManager: FileManager 32 | 33 | init(_ file: URL, fileManager: FileManager) { 34 | self.file = file 35 | self.fileManager = fileManager 36 | } 37 | 38 | func reset() { 39 | fileManager.createFile(atPath: file.path, contents: nil, attributes: nil) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Dependencies/DependenciesMapping.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | enum DependenciesMapping { 23 | /// Specifies which ENVs should be rewritten in the dependencies generation to make generic (paths agnostics) 24 | /// list of dependencies 25 | static let rewrittenEnvs = ["BUILD_DIR", "SRCROOT"] 26 | } 27 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Dependencies/FileListScanner.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | protocol FileListScanner { 23 | /// Returns true if the url is present in the file list 24 | func contains(_ url: URL) throws -> Bool 25 | } 26 | 27 | /// Finds file on a list of files provied by ListReader 28 | class FileListScannerImpl: FileListScanner { 29 | private let fileList: ListReader 30 | private let caseSensitive: Bool 31 | 32 | init(_ fileList: ListReader, caseSensitive: Bool) { 33 | self.fileList = fileList 34 | self.caseSensitive = caseSensitive 35 | } 36 | 37 | func contains(_ url: URL) throws -> Bool { 38 | if caseSensitive { 39 | return try fileList.listFilesURLs().contains(url) 40 | } 41 | let lowerCasePath = url.path.lowercased() 42 | return try fileList.listFilesURLs().lazy.contains { element in 43 | element.path.lowercased() == lowerCasePath 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Dependencies/StaticFileListReader.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | class StaticFileListReader: ListReader { 23 | private let list: [URL] 24 | 25 | init(list: [URL]) { 26 | self.list = list 27 | } 28 | 29 | func listFilesURLs() throws -> [URL] { 30 | list 31 | } 32 | 33 | func canRead() -> Bool { 34 | true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/FileAccess/Copier/CopyDiskCopier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Copier that physically copies files (as duplicates) 23 | class CopyDiskCopier: DiskCopier { 24 | private let fileManager: FileManager 25 | 26 | init(fileManager: FileManager) { 27 | self.fileManager = fileManager 28 | } 29 | 30 | func copy(file source: URL, destination: URL) throws { 31 | let parent = destination.deletingLastPathComponent() 32 | if !fileManager.fileExists(atPath: parent.path) { 33 | try fileManager.createDirectory(at: parent, withIntermediateDirectories: true, attributes: nil) 34 | } 35 | try fileManager.spt_forceCopyItem(at: source, to: destination) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/FileAccess/Copier/DiskCopier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Copier that moves files between two locations 23 | protocol DiskCopier { 24 | func copy(file source: URL, destination: URL) throws 25 | } 26 | 27 | extension DiskCopier { 28 | /// Moves item to the directory with the same name as in the source (mimic the `cp` behaviour) 29 | func copy(file source: URL, directory: URL) throws { 30 | let fileName = source.lastPathComponent 31 | let destination = directory.appendingPathComponent(fileName) 32 | try copy(file: source, destination: destination) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/FileAccess/Copier/HardLinkDiskCopier.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Copier that uses hard links 23 | class HardLinkDiskCopier: DiskCopier { 24 | private let fileManager: FileManager 25 | 26 | init(fileManager: FileManager) { 27 | self.fileManager = fileManager 28 | } 29 | 30 | func copy(file source: URL, destination: URL) throws { 31 | try fileManager.spt_forceLinkItem(at: source, to: destination) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Fingerprint/FingerprintAccumulator.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | public typealias RawFingerprint = String 23 | public typealias ContextSpecificFingerprint = String 24 | 25 | public struct Fingerprint { 26 | /// Raw fingerprint 27 | let raw: RawFingerprint 28 | /// Raw fingerprint interleaved with the env context 29 | let contextSpecific: ContextSpecificFingerprint 30 | } 31 | 32 | enum FingerprintAccumulatorError: Error { 33 | case missingFile(URL) 34 | } 35 | 36 | /// Fingerprint generator that produces a raw String 37 | public protocol FingerprintAccumulator { 38 | func reset() 39 | func append(_ content: String) throws 40 | func append(_ file: URL) throws 41 | func generate() throws -> RawFingerprint 42 | } 43 | 44 | /// Generator of the fingerprint that includes a context/environment aware fingerprint 45 | public protocol ContextAwareFingerprintAccumulator: FingerprintAccumulator { 46 | func generate() throws -> Fingerprint 47 | } 48 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/FlowControl/RemoteCommitInfo.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | enum RemoteCommitInfo: Equatable { 21 | /// No commit to use for the remote cache - remote cache is disabled 22 | case unavailable 23 | /// Valid remote commit sha to reuse artifacts is available 24 | case available(commit: String) 25 | } 26 | 27 | extension RemoteCommitInfo { 28 | init(_ commit: String?) { 29 | switch commit { 30 | case .some(let value) where !value.isEmpty: 31 | self = .available(commit: value) 32 | default: 33 | self = .unavailable 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Git/GitCommitManager.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Manages a storage for a remote-cache enabled commit 23 | protocol GitCommitManager { 24 | func readCacheCommit() throws -> String 25 | } 26 | 27 | /// Manages a commit on in a file 28 | class FileBackedGitCommitManager: GitCommitManager { 29 | private let file: URL 30 | 31 | init(_ file: URL) { 32 | self.file = file 33 | } 34 | 35 | func readCacheCommit() throws -> String { 36 | return try String(contentsOf: file).trim() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Models/MainArtifactMeta.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | protocol Meta: Codable { 21 | /// Unique id of the artifact 22 | var fileKey: String { get } 23 | } 24 | 25 | struct MainArtifactMeta: Meta, Equatable { 26 | /// List of all files used in the compilation 27 | var dependencies: [String] 28 | var fileKey: String 29 | /// Dependencies files raw fingerprint digest 30 | var rawFingerprint: String 31 | /// Commit sha that generated a product 32 | var generationCommit: String 33 | /// Name of the target 34 | var targetName: String 35 | /// Configuration used in the build 36 | var configuration: String 37 | /// Platform used in the build 38 | var platform: String 39 | /// Xcode build number generated the product 40 | var xcode: String 41 | /// All compilation files 42 | var inputs: [String] 43 | /// Extra keys added by meta plugins 44 | var pluginsKeys: [String: String] 45 | } 46 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Models/MetaWriter.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | protocol MetaWriter { 23 | func write(_ meta: T, locationDir: URL) throws -> URL where T: Meta 24 | } 25 | 26 | class JsonMetaWriter: MetaWriter { 27 | private let metaEncoder: JSONEncoder 28 | private let fileWriter: FileWriter 29 | 30 | init(fileWriter: FileWriter, pretty: Bool) { 31 | self.fileWriter = fileWriter 32 | let encoder = JSONEncoder() 33 | if pretty { 34 | encoder.outputFormatting = .prettyPrinted 35 | } 36 | self.metaEncoder = encoder 37 | } 38 | 39 | func write(_ meta: T, locationDir: URL) throws -> URL where T: Meta { 40 | let metaURL = locationDir.appendingPathComponent(meta.fileKey).appendingPathExtension("json") 41 | let metaData = try metaEncoder.encode(meta) 42 | try fileWriter.write(toPath: metaURL.path, contents: metaData) 43 | return metaURL 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Network/Authentication/AWSV4SigningKey.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | struct AWSV4SigningKey { 23 | 24 | let secretAccessKey: String 25 | let region: String 26 | let service: String 27 | let date: Date 28 | 29 | 30 | var value: [UInt8] { 31 | let formattedDate = StringToSign.ISO8601DateDayOnlyFormatter.string(from: date) 32 | 33 | let encryptedDate = HMAC.calcHMAC(keyString: "AWS4\(secretAccessKey)", value: formattedDate) 34 | let encryptedRegion = HMAC.calcHMAC(keyArray: encryptedDate, value: region) 35 | let encryptedService = HMAC.calcHMAC(keyArray: encryptedRegion, value: service) 36 | let encryptedSignature = HMAC.calcHMAC(keyArray: encryptedService, value: "aws4_request") 37 | 38 | return encryptedSignature 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Network/Authentication/SHA256.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import CommonCrypto 21 | import Foundation 22 | 23 | extension Data { 24 | 25 | func sha256() -> String { 26 | let hashData = digest(self) 27 | return hashData.map { String(format: "%02hhx", $0) }.joined() 28 | } 29 | 30 | private func digest(_ data: Data) -> Data { 31 | var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) 32 | _ = data.withUnsafeBytes { 33 | CC_SHA256($0.baseAddress, UInt32(data.count), &hash) 34 | } 35 | return Data(hash) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Network/IgnoringCertificatesTrustManager.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | final class IgnoringCertificatesTrustManager: NSObject, URLSessionDelegate { 23 | func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { 24 | guard let serverTrust = challenge.protectionSpace.serverTrust else { 25 | completionHandler(.performDefaultHandling, nil) 26 | return 27 | } 28 | 29 | let urlCredential = URLCredential(trust: serverTrust) 30 | completionHandler(.useCredential, urlCredential) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Network/LocalURLBuilder.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Builds local URL location for a remote url 23 | protocol LocalURLBuilder { 24 | func location(for url: URL) -> URL 25 | } 26 | 27 | /// Builds locally cached location for the remote url 28 | class LocalURLBuilderImpl: LocalURLBuilder { 29 | /// Application-specific location to place all cache files 30 | private static let remoteCacheDir = "XCRemoteCache" 31 | let localAddress: URL 32 | 33 | init(cachePath: URL) { 34 | localAddress = cachePath.appendingPathComponent(Self.remoteCacheDir) 35 | } 36 | 37 | func location(for url: URL) -> URL { 38 | let components = ([url.host] + url.pathComponents).compactMap { $0 } 39 | return components.reduce(localAddress) { prev, component in 40 | prev.appendingPathComponent(component) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Output/FilteredInvocationStorage.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | /// Filters retrieved invocations 21 | struct FilteredInvocationStorage: InvocationStorage { 22 | /// Underlying storage 23 | let storage: InvocationStorage 24 | /// List of commands that shouldn't be returned from the `retrieveAll` 25 | let retrieveIgnoredCommands: [String] 26 | 27 | func store(args: [String]) throws { 28 | try storage.store(args: args) 29 | } 30 | 31 | func retrieveAll() throws -> [[String]] { 32 | let allInvocations = try storage.retrieveAll() 33 | return try allInvocations.filter { invocation in 34 | guard let command = invocation.first else { 35 | throw InvocationStorageError.corruptedStorage 36 | } 37 | return !retrieveIgnoredCommands.contains(command) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Output/XCEncoders/XCEncoderAbstractFactory.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | // Builds concrete `XCRemoteCacheEncoder` 21 | class XCEncoderAbstractFactory { 22 | /// Builds concrete implementation `XCRemoteCacheEncoder` for specific output format 23 | /// - Parameter outputType: output format to 24 | /// - Returns: encoder to use 25 | func build(for format: XCOutputFormat) -> XCRemoteCacheEncoder { 26 | switch format { 27 | case .json: 28 | return XCJSONEncoder() 29 | case .yaml: 30 | return XCYAMLEncoder() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Output/XCEncoders/XCJSONEncoder.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Creates response in a json, human friendly format 23 | class XCJSONEncoder: XCRemoteCacheEncoder { 24 | private let encoder: JSONEncoder 25 | init() { 26 | let encoder = JSONEncoder() 27 | encoder.keyEncodingStrategy = .convertToSnakeCase 28 | encoder.outputFormatting = .prettyPrinted 29 | self.encoder = encoder 30 | } 31 | 32 | func encode(_ value: T) throws -> String where T: Encodable { 33 | let data = try encoder.encode(value) 34 | guard let stringRepresentation = String(data: data, encoding: .utf8) else { 35 | throw XCRemoteCacheEncoderError.cannotRepresentOutput 36 | } 37 | return stringRepresentation 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Output/XCEncoders/XCYAMLEncoder.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Yams 21 | 22 | /// Creates response in a yaml format 23 | class XCYAMLEncoder: XCRemoteCacheEncoder { 24 | private let encoder: YAMLEncoder 25 | init() { 26 | encoder = YAMLEncoder() 27 | } 28 | 29 | func encode(_ value: T) throws -> String where T: Encodable { 30 | return try encoder.encode(value) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Output/XCOutputFormat.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | /// Format of the standard output message 21 | public enum XCOutputFormat { 22 | case json 23 | case yaml 24 | } 25 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Output/XCRemoteCacheEncoder.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | enum XCRemoteCacheEncoderError: Error { 21 | /// Encoded data is not representable as a String 22 | case cannotRepresentOutput 23 | } 24 | 25 | /// Simplified encoder that creates String output of the response 26 | protocol XCRemoteCacheEncoder { 27 | /// Encodes an instance to the String 28 | /// - Parameter value: value to encode 29 | /// - Throws: XCRemoteCacheEncoderError 30 | func encode(_ value: T) throws -> String 31 | } 32 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Shell/ShellCommandsProcessor.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Allows to patch Command arguments 23 | protocol ArgsRewriter { 24 | /// Creates new invocation arguments 25 | /// - Parameter args: original command invocation args 26 | /// - Returns: command args with 27 | func applyArgsRewrite(_ args: [String]) throws -> [String] 28 | } 29 | 30 | /// Manages shell command invocations. Has a right to modify input args 31 | /// and process command's result in a post-action 32 | protocol ShellCommandsProcessor: ArgsRewriter { 33 | /// Called when the shell command finished with a success 34 | /// It adds a chance to read or modify output 35 | func postCommandProcessing() throws 36 | } 37 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Stats/StatsCoordinator.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | /// Logger to log events to the statistics database 21 | protocol StatsLogger { 22 | /// Increments a counter related to the action 23 | func log(_ event: XCRemoteCacheStatistics.Counter) throws 24 | } 25 | 26 | /// Coordinator to read and modify XCRemoteCache statistics 27 | protocol StatsCoordinator: StatsLogger { 28 | /// Fetches all statistics 29 | func readStats() throws -> XCRemoteCacheStatistics 30 | /// Resets all statistic counters 31 | func reset() throws 32 | } 33 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Utils/Array+Utils.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | public extension Array { 23 | func get(_ i: Index) -> Element? { 24 | guard count > i else { 25 | return nil 26 | } 27 | return self[i] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Utils/Date+Utils.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | public extension Date { 23 | func daysAgo(days: Int) -> Date? { 24 | var components = DateComponents() 25 | components.day = -days 26 | let newDate = Calendar.current.date(byAdding: components, to: self) 27 | return newDate 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Utils/FileTouch.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Manages file attributes (e.g. modification, creation date) 23 | public protocol Touch { 24 | /// Updates file modification date or creates empty one (if not existing) 25 | func touch() throws 26 | } 27 | 28 | public class FileTouch: Touch { 29 | private let filePath: String 30 | private let fileManager: FileManager 31 | 32 | public init(_ file: URL, fileManager: FileManager) { 33 | filePath = file.path 34 | self.fileManager = fileManager 35 | } 36 | 37 | public func touch() throws { 38 | if fileManager.fileExists(atPath: filePath) { 39 | var attributes = try fileManager.attributesOfFileSystem(forPath: filePath) 40 | attributes[.modificationDate] = Date() 41 | try fileManager.setAttributes(attributes, ofItemAtPath: filePath) 42 | } else { 43 | fileManager.createFile(atPath: filePath, contents: nil, attributes: nil) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Utils/String+TileInPath.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | extension String { 23 | /// Replacement of NSString's `expandingTildeInPath` where `~` is replaced with a local home address 24 | /// - Returns: A string that resolves `~` character to $HOME 25 | var expandingTildeInPath: String { 26 | replacingOccurrences(of: "~", with: NSHomeDirectory()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Utils/URL+ThrowingInitializer.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | enum URLError: Error { 23 | case invalidURLFormat(String) 24 | } 25 | 26 | public extension URL { 27 | /// Builds URL from a string or throws an error 28 | /// - Parameter string: URL building string 29 | /// - Throws: `URLError` if the string is invalid 30 | /// - Returns: URL instance 31 | static func build(for string: String) throws -> URL { 32 | if let url = URL(string: string) { 33 | return url 34 | } 35 | throw URLError.invalidURLFormat(string) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/XCRemoteCache/Xcode/BuildActionType.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | /// Represents a type of a build invoked by the Xcode 23 | enum BuildActionType: String, Codable { 24 | /// Standard build 25 | case build 26 | /// An extra build, exclusive for indexing (Introduced in Xcode 13) 27 | case index = "indexbuild" 28 | /// Archive build 29 | case archive = "install" 30 | /// Unknown type, probably incompatible Xcode version used 31 | case unknown 32 | } 33 | -------------------------------------------------------------------------------- /Sources/xcld/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCRemoteCache 21 | 22 | XCLDMain().main() 23 | -------------------------------------------------------------------------------- /Sources/xcldplusplus/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCRemoteCache 21 | 22 | XCLDPlusPlusMain().main() 23 | -------------------------------------------------------------------------------- /Sources/xclibtool/XCLibtoolMain.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | import xclibtoolSupport 22 | import XCRemoteCache 23 | 24 | public enum XCLibtoolMainError: Error { 25 | case missingOutput 26 | case unsupportedMode 27 | } 28 | 29 | /// Wrapper for a `libtool` program that copies the build executable (e.g. .a) from a cached-downloaded location 30 | /// Fallbacks to a standard `libtool` when the Ramote cache is not applicable (e.g. modified sources) 31 | public class XCLibtoolMain { 32 | public init() { } 33 | 34 | public func main() { 35 | let args = ProcessInfo().arguments 36 | 37 | do { 38 | let mode = try XCLibtoolHelper.buildMode(args: Array(args.dropFirst())) 39 | try XCLibtool(mode).run() 40 | } catch { 41 | exit(1, "Failed with: \(error). Args: \(args)") 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/xclibtool/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCRemoteCache 21 | 22 | XCLibtoolMain().main() 23 | -------------------------------------------------------------------------------- /Sources/xclipo/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCRemoteCache 21 | 22 | XCLipoMain().main() 23 | -------------------------------------------------------------------------------- /Sources/xcpostbuild/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCRemoteCache 21 | 22 | // Extra Xcode buildstep that decorates all produced non reproducible, machine-sensitive files (e.g. .swiftmodule) 23 | // with a custom fingerprint override (with .md5 file) 24 | // For a producer mode, it also builds an artifact package (.zip) and uploads it to the remote cache, along the 25 | // build artifact metatadata json 26 | XCPostbuild().main() 27 | -------------------------------------------------------------------------------- /Sources/xcprebuild/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCRemoteCache 21 | 22 | // Extra Xcode buildstep that verifies if the remotely available artifact can be used locally 23 | // (1) Fetches meta json for a currently used commit, compares a local fingerprint of all dependencies found 24 | // during a build and (2) unzips the artifact product to the target temporary directory 25 | // (3) Outputs marker file (e.g. enabled.rc in $TARGET_TEMP_DIR): 26 | // - removes a file when the remote cache cannot be used locally 27 | // - includes a list of all files that compilation steps should include to their .d output dependencies 28 | XCPrebuild().main() 29 | -------------------------------------------------------------------------------- /Sources/xcprepare/ArgumentsSupport.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import ArgumentParser 21 | import XCRemoteCache 22 | 23 | extension XCOutputFormat: ExpressibleByArgument { 24 | public init?(argument: String) { 25 | switch argument { 26 | case "json": 27 | self = .json 28 | case "yaml": 29 | self = .yaml 30 | default: 31 | return nil 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/xcprepare/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | XCPrepareMain.main() 21 | -------------------------------------------------------------------------------- /Sources/xcswift-frontend/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCRemoteCache 21 | 22 | XCSwiftcFrontendMain().main() 23 | -------------------------------------------------------------------------------- /Sources/xcswiftc/main.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCRemoteCache 21 | 22 | XCSwiftcMain().main() 23 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCTest 21 | 22 | import XCRemoteCacheTests 23 | 24 | var tests = [XCTestCaseEntry]() 25 | tests += XCRemoteCacheTests.allTests() 26 | XCTMain(tests) 27 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Commands/RemoteCommitInfoTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | import XCTest 22 | 23 | class RemoteCommitInfoTests: XCTestCase { 24 | 25 | func testEmptyCommitFallbacksToUnavailable() { 26 | XCTAssertEqual(RemoteCommitInfo(""), .unavailable) 27 | } 28 | 29 | func testNilCommitFallbacksToUnavailable() { 30 | XCTAssertEqual(RemoteCommitInfo(nil), .unavailable) 31 | } 32 | 33 | func testNonEmptyCommitIsAccepted() { 34 | XCTAssertEqual(RemoteCommitInfo("aba"), .available(commit: "aba")) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Config/ModeTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | import XCTest 22 | import Yams 23 | 24 | class ModeTests: XCTestCase { 25 | 26 | func testProducerFast() throws { 27 | let yaml = "producer-fast" 28 | let decoder = YAMLDecoder(encoding: .utf8) 29 | let mode: Mode = try decoder.decode(from: yaml) 30 | 31 | XCTAssertEqual(mode, .producerFast) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Dependencies/DependenciesWriterTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | import XCTest 22 | 23 | class FileDependenciesWriterTests: XCTestCase { 24 | 25 | private func generateTempFileURL(name: String = #function) throws -> URL { 26 | let directory = NSTemporaryDirectory() 27 | return try NSURL.fileURL(withPathComponents: [directory, name]).unwrap() 28 | } 29 | 30 | func testWriteDependencyWithSpace() throws { 31 | let url = try generateTempFileURL() 32 | let writer = FileDependenciesWriter(url, accessor: FileManager.default) 33 | 34 | try writer.writeGeneric(dependencies: [ 35 | "/SomePath/Pods/Target Support Files/lottie-ios/lottie-ios-dummy.m", 36 | ]) 37 | 38 | let expectedContent = """ 39 | dependencies: /SomePath/Pods/Target\\ Support\\ Files/lottie-ios/lottie-ios-dummy.m 40 | 41 | """ 42 | 43 | let content = String(data: try Data(contentsOf: url), encoding: .utf8) 44 | 45 | XCTAssertEqual(content, expectedContent) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Dependencies/StaticFileListReaderTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | import XCTest 22 | 23 | class StaticFileListReaderTests: XCTestCase { 24 | func testCanAlwaysRead() throws { 25 | let reader = StaticFileListReader(list: []) 26 | 27 | XCTAssertTrue(reader.canRead()) 28 | } 29 | 30 | func testListsPassedUrls() throws { 31 | let url: URL = "/file" 32 | let reader = StaticFileListReader(list: [url]) 33 | 34 | XCTAssertEqual(try reader.listFilesURLs(), [url]) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Helpers/Atomic.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | 23 | /// Multithread-safe reading&writing value wrapper 24 | @propertyWrapper 25 | struct Atomic { 26 | 27 | private var value: Value 28 | private let lock = NSLock() 29 | 30 | init(wrappedValue value: Value) { 31 | self.value = value 32 | } 33 | 34 | var wrappedValue: Value { 35 | get { 36 | lock.lock() 37 | defer { lock.unlock() } 38 | return value 39 | } 40 | set { 41 | lock.lock() 42 | defer { lock.unlock() } 43 | value = newValue 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Helpers/DataHelpers.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | extension Data: ExpressibleByStringLiteral { 23 | public init(stringLiteral value: String) { 24 | self = value.data(using: .utf8)! 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Helpers/OptionalHelpers.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | extension Optional { 21 | func unwrap() throws -> Wrapped { 22 | switch self { 23 | case .some(let value): 24 | return value 25 | default: 26 | throw "Unwrap failed" 27 | } 28 | } 29 | } 30 | 31 | extension String: Error {} 32 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Helpers/ProcessHelpers.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | private func which(_ cmd: String) throws -> String { 24 | return try shellGetStdout("/usr/bin/which", args: [cmd]) 25 | } 26 | 27 | /// Triggers a command without waiting it to finish 28 | func startExec(_ cmd: String, args: [String] = [], inDir dir: String? = nil) throws -> Process { 29 | let absCmd = try cmd.starts(with: "/") ? cmd : which(cmd) 30 | 31 | let task = Process() 32 | 33 | task.launchPath = absCmd 34 | task.arguments = args 35 | task.standardError = Process().standardError 36 | task.standardOutput = Process().standardOutput 37 | if let dir = dir { 38 | task.currentDirectoryPath = dir 39 | } 40 | task.launch() 41 | return task 42 | } 43 | 44 | /// Waits for a process finish 45 | func waitFor(_ task: Process) -> Int32 { 46 | task.waitUntilExit() 47 | return task.terminationStatus 48 | } 49 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Helpers/URLStringExpressive.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | extension URL: ExpressibleByStringLiteral { 23 | public typealias StringLiteralType = String 24 | 25 | public init(stringLiteral value: String) { 26 | self.init(fileURLWithPath: value) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Network/Authentication/AWSV4SigningKeyTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | import XCTest 23 | 24 | class AWSV4SigningKeyTest: XCTestCase { 25 | 26 | // Example from: 27 | // https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-other 28 | func testSigningKeyExample() throws { 29 | let signature = AWSV4SigningKey( 30 | secretAccessKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", 31 | region: "us-east-1", 32 | service: "iam", 33 | date: Date(timeIntervalSince1970: 1_329_267_660) 34 | ) 35 | 36 | XCTAssertEqual( 37 | signature.value.map { String(format: "%02hhx", $0) }.joined(), 38 | "f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d" 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Network/Authentication/StringToSignTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | import XCTest 23 | 24 | class StringToSignTest: XCTestCase { 25 | 26 | func testSimpleStringToSign() throws { 27 | let stringToSign = StringToSign( 28 | region: "us-east-1", 29 | service: "service1", 30 | canonicalRequestHash: "f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59", 31 | date: Date(timeIntervalSince1970: 1_624_524_656) 32 | ) 33 | 34 | XCTAssertEqual( 35 | stringToSign.value, 36 | "AWS4-HMAC-SHA256\n" + 37 | "20210624T085056Z\n" + 38 | "20210624/us-east-1/service1/aws4_request\n" + 39 | "f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59" 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Network/LocalURLBuilderImplTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | import XCTest 22 | 23 | class LocalURLBuilderImplTests: XCTestCase { 24 | private var invalidator: LocalURLBuilderImpl! 25 | 26 | func testBuilderWrapsAllRequestsWithAppDirectory() throws { 27 | let cacheURL = URL(fileURLWithPath: "/cache") 28 | let urlBuilder = LocalURLBuilderImpl(cachePath: cacheURL) 29 | let remoteURL = try URL(string: "https://address.com/path").unwrap() 30 | let expectedLocalURL = URL(fileURLWithPath: "/cache/XCRemoteCache/address.com/path") 31 | 32 | let localLocation = urlBuilder.location(for: remoteURL) 33 | 34 | XCTAssertEqual(localLocation, expectedLocalURL) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/ShellTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | import XCTest 22 | 23 | class ShellTests: XCTestCase { 24 | 25 | func testSellCallSucceeds() { 26 | XCTAssertNoThrow(try shellCall("/usr/bin/true", args: [], inDir: nil, environment: nil)) 27 | } 28 | 29 | func testSellFailedCallThrows() { 30 | XCTAssertThrowsError(try shellCall("/usr/bin/false", args: [], inDir: nil, environment: nil)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestData/Dependencies/AssetsFileDependenciesReaderTests/assetcatalog_dependencies_sample: -------------------------------------------------------------------------------- 1 | actool-22113.1/StandaloneApp/Assets.xcassets@/DerivedData/StandaloneApp/Build/Intermediates.noindex/StandaloneApp.build/Debug-iphonesimulator/StandaloneApp.build/assetcatalog_generated_info.plist@/DerivedData/StandaloneApp/Build/Products/Debug-iphonesimulator/StandaloneApp.app/Assets.car 2 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestData/Dependencies/JsonOverlayReaderTests/overlayReaderDefault.json: -------------------------------------------------------------------------------- 1 | { 2 | "case-sensitive": "false", 3 | "roots": 4 | [ 5 | { 6 | "contents": 7 | [ 8 | { 9 | "external-contents": "/Path/Target1/Target1.h", 10 | "name": "Target1.h", 11 | "type": "file", 12 | }, 13 | ], 14 | "name": "/DerivedDataProducts/Target1.framework/Headers", 15 | "type": "directory", 16 | }, 17 | { 18 | "contents": 19 | [ 20 | { 21 | "external-contents": "/DerivedDataIntermediate/Target2.build/module.modulemap", 22 | "name": "module.modulemap", 23 | "type": "file", 24 | }, 25 | ], 26 | "name": "/DerivedDataProducts/Target2.framework/Modules", 27 | "type": "directory", 28 | }, 29 | ], 30 | "version": 0, 31 | } 32 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestData/Dependencies/JsonOverlayReaderTests/overlayReaderEmpty.json: -------------------------------------------------------------------------------- 1 | {"case-sensitive":"false","roots":[],"version":0} 2 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/ActionSwiftcProductGenerationPlugin.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class ActionSwiftcProductGenerationPlugin: SwiftcProductGenerationPlugin { 24 | private let action: () throws -> Void 25 | 26 | init(_ action: @escaping () throws -> Void) { 27 | self.action = action 28 | } 29 | 30 | func generate(for: SwiftCompilationInfo) throws { 31 | try action() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/ArtifactConsumerPostbuildPluginSpy.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class ArtifactConsumerPostbuildPluginSpy: ArtifactConsumerPostbuildPlugin { 24 | private(set) var runInvocations: [MainArtifactMeta] = [] 25 | 26 | func run(meta: MainArtifactMeta) throws { 27 | runInvocations.append(meta) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/CCWrapperBuilderFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class CCWrapperBuilderFake: CCWrapperBuilder { 24 | func compile(to destination: URL, commitSha: String) throws {} 25 | } 26 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/CacheInvalidatorFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class CacheInvalidatorFake: CacheInvalidator { 24 | func invalidateArtifacts() {} 25 | } 26 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/CacheModeControllerFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | 22 | @testable import XCRemoteCache 23 | 24 | class CacheModeControllerFake: CacheModeController { 25 | 26 | var enabled = false 27 | var disabled = false 28 | var shouldDisable = false 29 | var dependencies: [URL] = [] 30 | 31 | func enable(allowedInputFiles: [URL], dependencies: [URL]) throws { 32 | enabled = true 33 | self.dependencies = dependencies 34 | } 35 | 36 | func disable() throws { 37 | disabled = true 38 | } 39 | 40 | func isEnabled() throws -> Bool { 41 | return enabled 42 | } 43 | 44 | func shouldDisable(for commit: RemoteCommitInfo) -> Bool { 45 | return shouldDisable 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/CountersFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | 22 | class CountersFake: Counters { 23 | private var counters: [Int] 24 | 25 | init(_ size: Int) { 26 | counters = Array(repeating: 0, count: size) 27 | } 28 | 29 | func readCounters() throws -> [Int] { 30 | return counters 31 | } 32 | 33 | func writeCounters(_ counters: [Int]) throws { 34 | self.counters = counters 35 | } 36 | 37 | func reset() throws { 38 | counters = Array(repeating: 0, count: counters.count) 39 | } 40 | 41 | func bumpCounter(position: Int) throws { 42 | counters[position] += 1 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/DSYMOrganizerFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class DSYMOrganizerFake: DSYMOrganizer { 24 | let dSYMFile: URL? 25 | let fileManager: FileManager 26 | 27 | init(dSYMFile: URL?, fileManager: FileManager = .default) { 28 | self.dSYMFile = dSYMFile 29 | self.fileManager = fileManager 30 | } 31 | 32 | func relevantDSYMLocation() throws -> URL? { 33 | guard let url = dSYMFile else { 34 | return nil 35 | } 36 | fileManager.createFile(atPath: url.path, contents: nil, attributes: nil) 37 | return dSYMFile 38 | } 39 | 40 | func syncDSYM(artifactPath: URL) throws { 41 | guard let dsym = dSYMFile else { 42 | return 43 | } 44 | try fileManager.spt_forceLinkItem(at: artifactPath, to: dsym) 45 | } 46 | 47 | func cleanup() throws { 48 | guard let dsym = dSYMFile else { 49 | return 50 | } 51 | try fileManager.removeItem(at: dsym) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/DependenciesReaderFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class DependenciesReaderFake: DependenciesReader { 24 | private let dependencies: [String: [String]] 25 | init(dependencies: [String: [String]]) { 26 | self.dependencies = dependencies 27 | } 28 | 29 | func findDependencies() throws -> [String] { 30 | return dependencies.values.flatMap { $0 } 31 | } 32 | 33 | func findInputs() throws -> [String] { 34 | return [] 35 | } 36 | 37 | func readFilesAndDependencies() throws -> [String: [String]] { 38 | return dependencies 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/DependenciesRemapperFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class DependenciesRemapperFake: DependenciesRemapper { 24 | private let baseURL: URL 25 | init(baseURL: URL) { 26 | self.baseURL = baseURL 27 | } 28 | 29 | func replace(genericPaths: [String]) -> [String] { 30 | genericPaths.map(baseURL.appendingPathComponent).map(\.path) 31 | } 32 | 33 | func replace(localPaths: [String]) -> [String] { 34 | localPaths.map { u -> String in 35 | let p = URL(fileURLWithPath: u, relativeTo: baseURL) 36 | return p.relativePath 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/DependenciesWriterSpy.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class DependenciesWriterSpy: DependenciesWriter { 24 | private(set) var wroteSkipForSha: String? 25 | func write(skipForSha: String) throws { 26 | wroteSkipForSha = skipForSha 27 | } 28 | 29 | private(set) var wroteDependencies: [String: [String]]? 30 | func write(dependencies: [String: [String]]) throws { 31 | wroteDependencies = dependencies 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/DestroyerArtifactProcessor.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | 21 | import Foundation 22 | @testable import XCRemoteCache 23 | 24 | /// A Processor fake that deletes the artifact 25 | class DestroyerArtifactProcessor: ArtifactProcessor { 26 | private let dirAccesor: DirAccessor 27 | 28 | init(_ dirAccesor: DirAccessor) { 29 | self.dirAccesor = dirAccesor 30 | } 31 | func process(rawArtifact url: URL) throws { 32 | try dirAccesor.removeItem(atPath: url.path) 33 | } 34 | func process(localArtifact url: URL) throws { 35 | try dirAccesor.removeItem(atPath: url.path) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/DisallowedExclusiveFileAccessor.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | // FileAcccessor that fails if one wants to acquire a lock 24 | class DisallowedExclusiveFileAccessor: ExclusiveFileAccessor { 25 | func exclusiveAccess(block: (FileHandle) throws -> (T)) throws -> T { 26 | throw "Invoked ProhibitedExclusiveFileAccessor" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/DiskArtifactCreator.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | /// Creator that saves artifacts on a disk within a workingDir location 24 | class DiskArtifactCreator: ArtifactSwiftProductsBuilderSpy, ArtifactCreator { 25 | private let workingDir: URL 26 | private let fileManager: FileManager 27 | 28 | init(workingDir: URL, buildingArtifact: URL, objcLocation: URL) { 29 | self.workingDir = workingDir 30 | fileManager = FileManager.default 31 | super.init(buildingArtifact: buildingArtifact, objcLocation: objcLocation) 32 | } 33 | 34 | func createArtifact(artifactKey: String, meta: MainArtifactMeta) throws -> Artifact { 35 | let metaURL = workingDir.appendingPathComponent(UUID().uuidString) 36 | let metaData = try JSONEncoder().encode(meta) 37 | try fileManager.spt_writeToFile(atPath: metaURL.path, contents: metaData) 38 | let artifact = Artifact(id: artifactKey, package: "", meta: metaURL) 39 | return artifact 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/DiskCopierFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | import XCTest 22 | 23 | class DiskCopierFake: DiskCopier { 24 | private let dirAccessor: DirAccessor 25 | 26 | init(dirAccessor: DirAccessor) { 27 | self.dirAccessor = dirAccessor 28 | } 29 | 30 | func copy(file source: URL, destination: URL) throws { 31 | try dirAccessor.write(toPath: destination.path, contents: dirAccessor.contents(atPath: source.path)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/ExtraArgumentShellCommandsProcessor.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class ExtraArgumentShellCommandsProcessor: ShellCommandsProcessor { 24 | private let extra: String 25 | 26 | init(_ extra: String) { 27 | self.extra = extra 28 | } 29 | 30 | func postCommandProcessing() throws {} 31 | 32 | func applyArgsRewrite(_ args: [String]) throws -> [String] { 33 | args + [extra] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/ExtraArtifactConsumerPlugin.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | /// Plugin that downloads an artifact with a suffix fileKey 24 | class ExtraArtifactConsumerPrebuildPlugin: ArtifactConsumerPrebuildPlugin { 25 | 26 | private let suffix: String 27 | private let placeToDownload: URL 28 | private let network: RemoteNetworkClient 29 | 30 | init(extraArtifactSuffix suffix: String, placeToDownload location: URL, network: RemoteNetworkClient) { 31 | self.suffix = suffix 32 | placeToDownload = location 33 | self.network = network 34 | } 35 | 36 | func run(meta: MainArtifactMeta) throws { 37 | let extraArtifactId = meta.fileKey.appending(suffix) 38 | let artifactPlaceToDownload = placeToDownload.appendingPathComponent(extraArtifactId) 39 | 40 | try network.download(.artifact(id: extraArtifactId), to: artifactPlaceToDownload) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/FileListScannerFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class FileListScannerFake: FileListScanner { 24 | private let files: [URL] 25 | init(files: [URL]) { 26 | self.files = files 27 | } 28 | 29 | func contains(_ url: URL) throws -> Bool { 30 | return files.contains(url) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/FileManagerFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class FileManagerFake: FileManager { 24 | var shouldReturnContentsOfFile = false 25 | override func contents(atPath path: String) -> Data? { 26 | return shouldReturnContentsOfFile ? Data() : nil 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/FingerprintAccumulatorFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class FingerprintAccumulatorFake: FingerprintAccumulator { 24 | private var appendedStrings: [String] = [] 25 | func append(_ content: String) throws { 26 | appendedStrings.append(content) 27 | } 28 | 29 | func reset() { 30 | appendedStrings = [] 31 | } 32 | 33 | func append(_ file: URL) throws { 34 | appendedStrings.append("FILE{\(file.path)}") 35 | } 36 | 37 | private(set) var generateCallsCount = 0 38 | func generate() throws -> RawFingerprint { 39 | defer { 40 | generateCallsCount += 1 41 | } 42 | return appendedStrings.joined(separator: ",") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/InMemoryGlobalCacheSwitcher.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class InMemoryGlobalCacheSwitcher: GlobalCacheSwitcher { 24 | enum State: Equatable { 25 | case enabled(sha: String) 26 | case disabled 27 | } 28 | 29 | private(set) var state = State.disabled 30 | 31 | func enable(sha: String) throws { 32 | state = .enabled(sha: sha) 33 | } 34 | 35 | func disable() throws { 36 | state = .disabled 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/InMemoryStatsCoordinator.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | /// Fake that manages all logs in memory and reports 0 cached bytes 24 | /// Note: This fake is thread unsafe 25 | class InMemoryStatsCoordinator: StatsCoordinator { 26 | private var counters = [XCRemoteCacheStatistics.Counter: Int]() 27 | 28 | func log(_ event: XCRemoteCacheStatistics.Counter) throws { 29 | counters[event, default: 0] += 1 30 | } 31 | 32 | func readStats() throws -> XCRemoteCacheStatistics { 33 | XCRemoteCacheStatistics( 34 | hitCount: counters[.targetCacheHit] ?? 0, 35 | missCount: counters[.targetCacheMiss] ?? 0, 36 | localCacheBytes: 0, 37 | indexingHitCount: counters[.indexingTargetHitCount] ?? 0, 38 | indexingMissCount: counters[.indexingTargetMissCount] ?? 0 39 | ) 40 | } 41 | 42 | func reset() throws { 43 | counters = [:] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/ListReaderFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class ListReaderFake: ListReader { 24 | private let files: [URL]? 25 | init(files: [URL]?) { 26 | self.files = files 27 | } 28 | 29 | func listFilesURLs() throws -> [URL] { 30 | return try files.unwrap() 31 | } 32 | 33 | func canRead() -> Bool { 34 | return files != nil 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/MainArtifactSampleMeta.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | enum MainArtifactSampleMeta { 24 | static let defaults = MainArtifactMeta( 25 | dependencies: [], 26 | fileKey: "fileKey", 27 | rawFingerprint: "", 28 | generationCommit: "", 29 | targetName: "", 30 | configuration: "", 31 | platform: "", 32 | xcode: "", 33 | inputs: [], 34 | pluginsKeys: [:] 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/MarkerWriterSpy.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class MarkerWriterSpy: MarkerWriter { 24 | enum State: Equatable { 25 | case initial 26 | case enabled([URL]) 27 | case disabled 28 | } 29 | 30 | private(set) var state: State = .initial 31 | func enable(dependencies: [URL]) throws { 32 | state = .enabled(dependencies) 33 | } 34 | 35 | func disable() throws { 36 | state = .disabled 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/NoopArtifactProcessor.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | 21 | import Foundation 22 | @testable import XCRemoteCache 23 | 24 | /// No-operation processor 25 | class NoopArtifactProcessor: ArtifactProcessor { 26 | func process(rawArtifact url: URL) throws {} 27 | func process(localArtifact url: URL) throws {} 28 | } 29 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/OverlayReaderFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class OverlayReaderFake: OverlayReader { 24 | var mappings: [OverlayMapping] 25 | 26 | init(mappings: [OverlayMapping]) { 27 | self.mappings = mappings 28 | } 29 | 30 | func provideMappings() throws -> [OverlayMapping] { 31 | return mappings 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/PostShellCommandsProcessor.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class PostShellCommandsProcessor: ShellCommandsProcessor { 24 | private let action: () throws -> Void 25 | init(_ action: @escaping () throws -> Void) { 26 | self.action = action 27 | } 28 | 29 | func postCommandProcessing() throws { 30 | try action() 31 | } 32 | 33 | func applyArgsRewrite(_ args: [String]) throws -> [String] { 34 | args 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/ShellOutSpy.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class ShellOutSpy: ShellOut { 24 | struct Invocation: Equatable { 25 | let command: String 26 | let args: [String] 27 | let envs: [String: String]? 28 | } 29 | 30 | private(set) var switchedProcess: Invocation? 31 | private(set) var calledProcesses: [Invocation] = [] 32 | 33 | func switchToExternalProcess(command: String, invocationArgs: [String]) { 34 | switchedProcess = Invocation(command: command, args: invocationArgs, envs: nil) 35 | } 36 | 37 | func callExternalProcessAndWait(command: String, invocationArgs: [String], envs: [String: String]) throws { 38 | calledProcesses.append(Invocation(command: command, args: invocationArgs, envs: envs)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/SwiftcInputReaderStub.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class SwiftcInputReaderStub: SwiftcInputReader { 24 | private let info: SwiftCompilationInfo 25 | init(info: SwiftCompilationInfo) { 26 | self.info = info 27 | } 28 | 29 | init() { 30 | let defaultCompilationInfo = SwiftModuleCompilationInfo( 31 | dependencies: nil, 32 | swiftDependencies: "" 33 | ) 34 | info = .init(info: defaultCompilationInfo, files: []) 35 | } 36 | 37 | func read() throws -> SwiftCompilationInfo { 38 | return info 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/SwiftcMock.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class SwiftcMock: SwiftcProtocol { 24 | private let result: SwiftCResult 25 | init(mockingResult: SwiftCResult) { 26 | result = mockingResult 27 | } 28 | 29 | func mockCompilation() throws -> SwiftCResult { 30 | return result 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/SwiftcProductsGeneratorSpy.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class SwiftcProductsGeneratorSpy: SwiftcProductsGenerator { 24 | private(set) var generated: [([SwiftmoduleFileExtension: URL], URL)] = [] 25 | private let generationDestination: SwiftcProductsGeneratorOutput 26 | 27 | init(generatedDestination: SwiftcProductsGeneratorOutput) { 28 | generationDestination = generatedDestination 29 | } 30 | 31 | func generateFrom( 32 | artifactSwiftModuleFiles: [SwiftmoduleFileExtension: URL], 33 | artifactSwiftModuleObjCFile: URL 34 | ) throws -> SwiftcProductsGeneratorOutput { 35 | generated.append(( 36 | artifactSwiftModuleFiles, 37 | artifactSwiftModuleObjCFile 38 | )) 39 | return generationDestination 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/ThinningConsumerArtifactOrganizerFakeFactory.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | import XCTest 22 | 23 | class ThinningConsumerArtifactOrganizerFakeFactory: ThinningConsumerArtifactsOrganizerFactory { 24 | func build(targetTempDir: URL) -> ArtifactOrganizer { 25 | ArtifactOrganizerFake(artifactRoot: targetTempDir.appendingPathComponent("active")) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/ThinningConsumerArtifactsOrganizerFakeFactory.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class ThinningConsumerArtifactsOrganizerFakeFactory: ThinningConsumerArtifactsOrganizerFactory { 24 | private(set) var builtOrganizers: [ArtifactOrganizerFake] = [] 25 | 26 | func build(targetTempDir: URL) -> ArtifactOrganizer { 27 | let organizer = ArtifactOrganizerFake(artifactRoot: targetTempDir) 28 | builtOrganizers.append(organizer) 29 | return organizer 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/ThinningConsumerSwiftProductsOrganizerFactoryFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | import XCTest 22 | 23 | class ThinningConsumerSwiftProductsOrganizerFactoryFake: ThinningConsumerSwiftProductsOrganizerFactory { 24 | private let arch: String 25 | private let generator: SwiftcProductsGenerator 26 | private let syncer: FingerprintSyncer 27 | 28 | init(arch: String, generator: SwiftcProductsGenerator, syncer: FingerprintSyncer) { 29 | self.arch = arch 30 | self.generator = generator 31 | self.syncer = syncer 32 | } 33 | 34 | func build(architecture: String, targetName: String, moduleName: String, artifactLocation: URL) -> SwiftProductsOrganizer { 35 | return UnzippedArtifactSwiftProductsOrganizer( 36 | arch: arch, 37 | moduleName: moduleName, 38 | artifactLocation: artifactLocation, 39 | productsGenerator: generator, 40 | fingerprintSyncer: syncer 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/TimeoutingNetworkClient.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class TimeoutingNetworkClient: NetworkClient { 24 | func fileExists(_ url: URL, completion: @escaping (Result) -> Void) { 25 | completion(.failure(.timeout)) 26 | } 27 | 28 | func fetch(_ url: URL, completion: @escaping (Result) -> Void) { 29 | completion(.failure(.timeout)) 30 | } 31 | 32 | func download(_ url: URL, to location: URL, completion: @escaping (Result) -> Void) { 33 | completion(.failure(.timeout)) 34 | } 35 | 36 | func upload(_ file: URL, as url: URL, completion: @escaping (Result) -> Void) { 37 | completion(.failure(.timeout)) 38 | } 39 | 40 | func create(_ url: URL, completion: @escaping (Result) -> Void) { 41 | completion(.failure(.timeout)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/TouchSpy.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class TouchSpy: Touch { 24 | private(set) var touched = false 25 | func touch() throws { 26 | touched = true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/URLBuilderFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class URLBuilderFake: URLBuilder { 24 | private let address: URL 25 | init(_ address: URL) { 26 | self.address = address 27 | } 28 | 29 | func location(for remote: RemoteCacheFile) throws -> URL { 30 | switch remote { 31 | case .artifact(id: let artifactId): 32 | return address.appendingPathComponent("file").appendingPathComponent(artifactId) 33 | case .marker(commit: let commit): 34 | return address.appendingPathComponent("marker").appendingPathComponent(commit) 35 | case .meta(commit: let commit): 36 | return address.appendingPathComponent("meta").appendingPathComponent(commit) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/TestDoubles/WorkerFake.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import Foundation 21 | @testable import XCRemoteCache 22 | 23 | class WorkerFake: Worker { 24 | private var errors: [Error] = [] 25 | 26 | func appendAction(_ action: @escaping () throws -> Void) { 27 | do { 28 | try action() 29 | } catch { 30 | errors.append(error) 31 | } 32 | } 33 | 34 | func waitForResult() -> WorkerResult { 35 | defer { 36 | errors = [] 37 | } 38 | if errors.isEmpty { 39 | return .successes 40 | } 41 | return .errors(errors) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/XCRemoteCacheTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCTest 21 | 22 | final class XCRemoteCacheTests: XCTestCase { 23 | 24 | func testExample() { 25 | XCTAssertEqual(1 + 1, 2) 26 | } 27 | 28 | static var allTests = [ 29 | ("testExample", testExample), 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import XCTest 21 | 22 | #if !os(macOS) 23 | public func allTests() -> [XCTestCaseEntry] { 24 | return [ 25 | testCase(XCRemoteCacheTests.allTests), 26 | ] 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /Tests/XCRemoteCacheTests/Xcode/XcodeProbeImplTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | @testable import XCRemoteCache 21 | 22 | import XCTest 23 | 24 | 25 | class XcodeProbeImplTests: XCTestCase { 26 | 27 | private func generateOutput(returnString: String) -> ShellOutFunction { 28 | return { _, _, _, _ in returnString } 29 | } 30 | 31 | func testValidOutput() throws { 32 | let outputString = """ 33 | Xcode 11.3.1 34 | Build version 11C505 35 | """ 36 | let probe = XcodeProbeImpl(shell: generateOutput(returnString: outputString)) 37 | 38 | let version = try probe.read() 39 | 40 | XCTAssertEqual(version.buildVersion, "11C505") 41 | XCTAssertEqual(version.version, "11.3.1") 42 | } 43 | 44 | func testFailingForInvalidOutput() throws { 45 | let outputString = "" 46 | let probe = XcodeProbeImpl(shell: generateOutput(returnString: outputString)) 47 | 48 | XCTAssertThrowsError(try probe.read()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /backend-example/Dockerfile: -------------------------------------------------------------------------------- 1 | # ================================ 2 | # Build image 3 | # ================================ 4 | FROM nginx:1.21.1 5 | 6 | COPY nginx.conf /etc/nginx/nginx.conf 7 | RUN mkdir -p /tmp/cache 8 | RUN chown -R nginx:nginx /tmp/cache 9 | -------------------------------------------------------------------------------- /backend-example/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | worker_processes 1; 3 | 4 | events { 5 | worker_connections 1024; 6 | } 7 | 8 | http { 9 | include mime.types; 10 | default_type application/octet-stream; 11 | sendfile on; 12 | 13 | keepalive_timeout 65; 14 | 15 | server { 16 | listen 8080; 17 | server_name localhost; 18 | 19 | location / { 20 | root html; 21 | index index.html index.htm; 22 | } 23 | 24 | location /cache/ { 25 | # The path to the directory where nginx should store the cache contents. 26 | root /tmp/cache; 27 | # Allow PUT 28 | dav_methods PUT; 29 | # Allow nginx to create the /ac and /cas subdirectories. 30 | create_full_put_path on; 31 | # The maximum size of a single file. 32 | client_max_body_size 1G; 33 | allow all; 34 | } 35 | 36 | error_page 500 502 503 504 /50x.html; 37 | location = /50x.html { 38 | root html; 39 | } 40 | 41 | } 42 | include servers/*; 43 | } 44 | -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: XCRemoteCache 5 | spec: 6 | type: library 7 | owner: foundation 8 | -------------------------------------------------------------------------------- /cocoapods-plugin/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in cocoapods-xcremotecache.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /cocoapods-plugin/Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | 3 | def specs(dir) 4 | FileList["spec/#{dir}/*_spec.rb"].shuffle.join(' ') 5 | end 6 | 7 | desc 'Runs all the specs' 8 | task :specs do 9 | sh "bundle exec bacon #{specs('**')}" 10 | end 11 | 12 | task :default => :specs 13 | -------------------------------------------------------------------------------- /cocoapods-plugin/cocoapods-xcremotecache.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'cocoapods-xcremotecache/gem_version.rb' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'cocoapods-xcremotecache' 8 | spec.version = CocoapodsXcremotecache::VERSION 9 | spec.authors = ['Bartosz Polaczyk', 'Mark Vasiv'] 10 | spec.email = ['bartoszp@spotify.com', 'mvasiv@spotify.com'] 11 | spec.description = %q{CocoaPods plugin that enables XCRemoteCache with the project.} 12 | spec.summary = %q{A simple plugin that attaches to the post_install hook and modifies the generated project to use XCRemoteCache. Supports both producing anc consuming parts.} 13 | spec.homepage = 'https://github.com/spotify/XCRemoteCache' 14 | spec.license = 'Apache-2.0' 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ['lib'] 20 | end 21 | -------------------------------------------------------------------------------- /cocoapods-plugin/lib/cocoapods-xcremotecache.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Spotify AB 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | require 'cocoapods-xcremotecache/gem_version' 16 | -------------------------------------------------------------------------------- /cocoapods-plugin/lib/cocoapods-xcremotecache/command.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Spotify AB 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | require 'cocoapods-xcremotecache/command/xcremotecache' 16 | require 'cocoapods-xcremotecache/command/hooks' 17 | require 'cocoapods-xcremotecache/command/podfile' 18 | -------------------------------------------------------------------------------- /cocoapods-plugin/lib/cocoapods-xcremotecache/command/podfile.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Spotify AB 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | require 'cocoapods' 16 | 17 | module Pod 18 | class Podfile 19 | module DSL 20 | 21 | def xcremotecache(configuration) 22 | CocoapodsXCRemoteCacheModifier::Hooks::XCRemoteCache.configure(configuration) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /cocoapods-plugin/lib/cocoapods-xcremotecache/gem_version.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Spotify AB 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | module CocoapodsXcremotecache 16 | VERSION = "0.0.18" 17 | end 18 | -------------------------------------------------------------------------------- /cocoapods-plugin/lib/cocoapods_plugin.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Spotify AB 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | require 'cocoapods-xcremotecache/command' 16 | -------------------------------------------------------------------------------- /docs/design/ArchitecturalDesigns.md: -------------------------------------------------------------------------------- 1 | # Architectural designs 2 | 3 | 1. [Swift Driver Integration](./SwiftDriverIntegration.md) 4 | -------------------------------------------------------------------------------- /docs/img/build-phases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/build-phases.png -------------------------------------------------------------------------------- /docs/img/build-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/build-settings.png -------------------------------------------------------------------------------- /docs/img/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/console.png -------------------------------------------------------------------------------- /docs/img/debug-breakpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/debug-breakpoint.png -------------------------------------------------------------------------------- /docs/img/debug-phase-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/debug-phase-update.png -------------------------------------------------------------------------------- /docs/img/debug-scheme-wait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/debug-scheme-wait.png -------------------------------------------------------------------------------- /docs/img/debug-scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/debug-scheme.png -------------------------------------------------------------------------------- /docs/img/debug-wait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/debug-wait.png -------------------------------------------------------------------------------- /docs/img/driver-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/driver-dark.png -------------------------------------------------------------------------------- /docs/img/driver-scenario1-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/driver-scenario1-dark.png -------------------------------------------------------------------------------- /docs/img/driver-scenario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/driver-scenario1.png -------------------------------------------------------------------------------- /docs/img/driver-scenario2-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/driver-scenario2-dark.png -------------------------------------------------------------------------------- /docs/img/driver-scenario2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/driver-scenario2.png -------------------------------------------------------------------------------- /docs/img/driver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/driver.png -------------------------------------------------------------------------------- /docs/img/dsym-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/dsym-default.png -------------------------------------------------------------------------------- /docs/img/dsym-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/dsym-warning.png -------------------------------------------------------------------------------- /docs/img/logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/logo-dark.png -------------------------------------------------------------------------------- /docs/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/logo.png -------------------------------------------------------------------------------- /docs/img/pre-driver-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/pre-driver-dark.png -------------------------------------------------------------------------------- /docs/img/pre-driver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/pre-driver.png -------------------------------------------------------------------------------- /docs/img/sample-driver-timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/XCRemoteCache/75bac3293a3e881d51788a8ad5a461c90eaf6b23/docs/img/sample-driver-timeline.png -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/.rcinfo: -------------------------------------------------------------------------------- 1 | --- 2 | cache_addresses: 3 | - 'http://localhost:8080/cache/pods' 4 | primary_repo: '.' 5 | primary_branch: 'e2e-test-branch' 6 | mode: 'consumer' 7 | final_target': XCRemoteCacheSample' 8 | artifact_maximum_age: 0 # do not use local cache in ~/Library/Caches/XCRemoteCache 9 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/MixedTarget/MixedTarget-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "SomeObjC.h" 6 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/MixedTarget/MixedTarget.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @objc 4 | public class SomeClass: NSObject { 5 | @objc public var someEnum: SomeEnum = .default 6 | } 7 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/MixedTarget/SomeObjC.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | typedef NS_ENUM(NSInteger, SomeEnum) { 7 | SomeEnumDefault 8 | }; 9 | 10 | NS_ASSUME_NONNULL_END 11 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @main 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | 7 | 8 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 9 | // Override point for customization after application launch. 10 | return true 11 | } 12 | 13 | // MARK: UISceneSession Lifecycle 14 | 15 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 16 | // Called when a new scene session is being created. 17 | // Use this method to select a configuration to create the new scene with. 18 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 19 | } 20 | 21 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 22 | // Called when the user discards a scene session. 23 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 24 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 25 | } 26 | 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | $(PRODUCT_MODULE_NAME).SceneDelegate 18 | UISceneStoryboardFile 19 | Main 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp/StandaloneObjc.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface StandaloneObjc : NSObject 6 | 7 | @end 8 | 9 | NS_ASSUME_NONNULL_END 10 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp/StandaloneObjc.m: -------------------------------------------------------------------------------- 1 | #import "StandaloneObjc.h" 2 | #import "MixedTarget/MixedTarget-Swift.h" 3 | 4 | @implementation StandaloneObjc 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StandaloneApp/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import MixedTarget 3 | 4 | class ViewController: UIViewController { 5 | 6 | override func viewDidLoad() { 7 | super.viewDidLoad() 8 | // Do any additional setup after loading the view. 9 | } 10 | 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StaticFramework/StaticFramework.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | //! Project version number for StaticFramework. 4 | FOUNDATION_EXPORT double StaticFrameworkVersionNumber; 5 | 6 | //! Project version string for StaticFramework. 7 | FOUNDATION_EXPORT const unsigned char StaticFrameworkVersionString[]; 8 | 9 | // In this header, you should import all the public headers of your framework using statements like #import 10 | 11 | 12 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/StaticFramework/StaticFrameworkFile.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class SomeClass { 4 | public init() {} 5 | } 6 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/WatchExtension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EXAppExtensionAttributes 6 | 7 | EXExtensionPointIdentifier 8 | com.apple.appintents-extension 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/WatchExtension/WatchExtension.swift: -------------------------------------------------------------------------------- 1 | import AppIntents 2 | 3 | -------------------------------------------------------------------------------- /e2eTests/StandaloneSampleApp/WatchExtension/WatchExtensionExtension.swift: -------------------------------------------------------------------------------- 1 | import AppIntents 2 | 3 | struct WatchExtensionExtension: AppIntentsExtension { 4 | } 5 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import UIKit 21 | 22 | @main 23 | class AppDelegate: UIResponder, UIApplicationDelegate { 24 | 25 | 26 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 27 | return true 28 | } 29 | 30 | // MARK: UISceneSession Lifecycle 31 | 32 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 33 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 34 | } 35 | 36 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | $(PRODUCT_MODULE_NAME).SceneDelegate 18 | UISceneStoryboardFile 19 | Main 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import UIKit 21 | 22 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 23 | var window: UIWindow? 24 | 25 | 26 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 27 | } 28 | 29 | func sceneDidDisconnect(_ scene: UIScene) { 30 | } 31 | 32 | func sceneDidBecomeActive(_ scene: UIScene) { 33 | } 34 | 35 | func sceneWillResignActive(_ scene: UIScene) { 36 | } 37 | 38 | func sceneWillEnterForeground(_ scene: UIScene) { 39 | } 40 | 41 | func sceneDidEnterBackground(_ scene: UIScene) { 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /e2eTests/XCRemoteCacheSample/XCRemoteCacheSample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Spotify AB. 2 | // 3 | // Licensed to the Apache Software Foundation (ASF) under one 4 | // or more contributor license agreements. See the NOTICE file 5 | // distributed with this work for additional information 6 | // regarding copyright ownership. The ASF licenses this file 7 | // to you under the Apache License, Version 2.0 (the 8 | // "License"); you may not use this file except in compliance 9 | // with the License. You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, 14 | // software distributed under the License is distributed on an 15 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. See the License for the 17 | // specific language governing permissions and limitations 18 | // under the License. 19 | 20 | import UIKit 21 | 22 | class ViewController: UIViewController { 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /e2eTests/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | # Nginx configuration used for E2E tests cache server 2 | # Listens HTTP on port 8080 3 | 4 | 5 | worker_processes 1; 6 | 7 | events { 8 | worker_connections 1024; 9 | } 10 | 11 | 12 | http { 13 | default_type application/octet-stream; 14 | 15 | server { 16 | listen 8080; 17 | server_name localhost; 18 | 19 | location / { 20 | root html; 21 | index index.html index.htm; 22 | } 23 | 24 | location /cache/ { 25 | # The path to the directory where nginx should store the cache contents. 26 | root /tmp/cache; 27 | # Allow PUT 28 | dav_methods PUT; 29 | create_full_put_path on; 30 | # The maximum size of a single file. 31 | client_max_body_size 1G; 32 | allow all; 33 | } 34 | sendfile on; 35 | 36 | keepalive_timeout 65; 37 | 38 | error_page 500 502 503 504 /50x.html; 39 | location = /50x.html { 40 | root html; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /e2eTests/tests/README.md: -------------------------------------------------------------------------------- 1 | ### How to add a CocoaPods scenario 2 | 3 | In order to run different test suites in cocoapods integration, you can create multiple `.Podfile` files as a "starting point" of the E2E test. 4 | 5 | #### Files organization 6 | 7 | 1. File `{SUITE_NAME}.Podfile` contains a base Podfile that should be excercised 8 | 1. [Optional] File `{SUITE_NAME}.Podfile.config` contains a hash of extra xcremote-cache-plugin parameters that should be added to the `xcremotecache()` configuration in a podfile 9 | 1. [Optional] File `{SUITE_NAME}.Podfile.expectations` overrides a set of default validation steps that should be checked after consumer's build. Supported steps are: `hits`, `misses` or `hit_rate` (e.g. `100` for 100$). By default, 100% `hit_rate` and 0 `misses` are used - if you want to skip a validation step for a given suite, you can set it to `null` in a dictionary 10 | -------------------------------------------------------------------------------- /e2eTests/tests/default.Podfile: -------------------------------------------------------------------------------- 1 | plugin 'cocoapods-xcremotecache' 2 | 3 | target 'XCRemoteCacheSample' do 4 | use_frameworks! 5 | 6 | pod 'Firebase/Analytics' 7 | pod 'ReactiveSwift' 8 | end 9 | -------------------------------------------------------------------------------- /e2eTests/tests/excludeIphone.Podfile: -------------------------------------------------------------------------------- 1 | plugin 'cocoapods-xcremotecache' 2 | 3 | target 'XCRemoteCacheSample' do 4 | use_frameworks! 5 | 6 | pod 'Firebase/Analytics' 7 | pod 'ReactiveSwift' 8 | end 9 | -------------------------------------------------------------------------------- /e2eTests/tests/excludeIphone.Podfile.config: -------------------------------------------------------------------------------- 1 | { 2 | "exclude_sdks_configurations": ["iphoneos*", "iphonesimulator*"] 3 | } 4 | -------------------------------------------------------------------------------- /e2eTests/tests/excludeIphone.Podfile.expectations: -------------------------------------------------------------------------------- 1 | { 2 | "hits": 0, 3 | "misses": 0, 4 | "hit_rate": null 5 | } 6 | -------------------------------------------------------------------------------- /e2eTests/tests/excludeWatch.Podfile: -------------------------------------------------------------------------------- 1 | plugin 'cocoapods-xcremotecache' 2 | 3 | target 'XCRemoteCacheSample' do 4 | use_frameworks! 5 | 6 | pod 'Firebase/Analytics' 7 | pod 'ReactiveSwift' 8 | end 9 | -------------------------------------------------------------------------------- /e2eTests/tests/excludeWatch.Podfile.config: -------------------------------------------------------------------------------- 1 | { 2 | "exclude_sdks_configurations": ["watchos*", "watchsimulator*"] 3 | } 4 | -------------------------------------------------------------------------------- /e2eTests/tests/multiplePods.Podfile: -------------------------------------------------------------------------------- 1 | plugin 'cocoapods-xcremotecache' 2 | 3 | install! 'cocoapods', :generate_multiple_pod_projects => true 4 | 5 | 6 | target 'XCRemoteCacheSample' do 7 | use_frameworks! 8 | 9 | pod 'Firebase/Analytics' 10 | pod 'ReactiveSwift' 11 | end 12 | --------------------------------------------------------------------------------