From 324630ea172f47630501489dc94760f1b7e5d1cf Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 22 Jun 2025 07:16:52 +0200 Subject: [PATCH] speed up project build by comparing web hash --- mac/VibeTunnel.xcodeproj/project.pbxproj | 26 +- .../project.pbxproj.backup | 589 ++++++++++++++++++ mac/scripts/build-web-frontend-conditional.sh | 145 +++++ mac/scripts/calculate-web-hash.sh | 55 ++ 4 files changed, 812 insertions(+), 3 deletions(-) create mode 100644 mac/VibeTunnel.xcodeproj/project.pbxproj.backup create mode 100755 mac/scripts/build-web-frontend-conditional.sh create mode 100755 mac/scripts/calculate-web-hash.sh diff --git a/mac/VibeTunnel.xcodeproj/project.pbxproj b/mac/VibeTunnel.xcodeproj/project.pbxproj index a95b9db4..505c354b 100644 --- a/mac/VibeTunnel.xcodeproj/project.pbxproj +++ b/mac/VibeTunnel.xcodeproj/project.pbxproj @@ -111,6 +111,7 @@ 788687ED2DFF4FCB00B22C15 /* Sources */, 788687EE2DFF4FCB00B22C15 /* Frameworks */, 788687EF2DFF4FCB00B22C15 /* Resources */, + A1B2C3D4E5F6789012345678 /* Calculate Web Hash */, B2C3D4E5F6A7B8C9D0E1F234 /* Build Web Frontend */, ); buildRules = ( @@ -211,7 +212,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - B2C3D4E5F6A7B8C9D0E1F234 /* Build Web Frontend */ = { + A1B2C3D4E5F6789012345678 /* Calculate Web Hash */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; @@ -220,7 +221,26 @@ inputFileListPaths = ( ); inputPaths = ( - "$(SRCROOT)/../web/", + ); + name = "Calculate Web Hash"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/.web-content-hash", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/zsh; + shellScript = "# Calculate hash of web content\necho \"Calculating web content hash...\"\n\n# Run the hash calculation script\n\"${SRCROOT}/scripts/calculate-web-hash.sh\"\n\nif [ $? -ne 0 ]; then\n echo \"error: Failed to calculate web hash\"\n exit 1\nfi\n"; + }; + B2C3D4E5F6A7B8C9D0E1F234 /* Build Web Frontend */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(BUILT_PRODUCTS_DIR)/.web-content-hash", ); name = "Build Web Frontend"; outputFileListPaths = ( @@ -231,7 +251,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/zsh; - shellScript = "# Build web frontend using Bun\necho \"Building web frontend...\"\n\n# Run the build script\n\"${SRCROOT}/scripts/build-web-frontend.sh\"\n\nif [ $? -ne 0 ]; then\n echo \"error: Web frontend build failed\"\n exit 1\nfi\n"; + shellScript = "# Build web frontend conditionally based on hash\necho \"Checking if web frontend needs rebuild...\"\n\n# Run the conditional build script\n\"${SRCROOT}/scripts/build-web-frontend-conditional.sh\"\n\nif [ $? -ne 0 ]; then\n echo \"error: Web frontend build failed\"\n exit 1\nfi\n"; }; C3D4E5F6A7B8C9D0E1F23456 /* Install Build Dependencies */ = { isa = PBXShellScriptBuildPhase; diff --git a/mac/VibeTunnel.xcodeproj/project.pbxproj.backup b/mac/VibeTunnel.xcodeproj/project.pbxproj.backup new file mode 100644 index 00000000..18b1922c --- /dev/null +++ b/mac/VibeTunnel.xcodeproj/project.pbxproj.backup @@ -0,0 +1,589 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 78AD8B952E051ED40009725C /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 78AD8B942E051ED40009725C /* Logging */; }; + 89D01D862CB5D7DC0075D8BD /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 89D01D852CB5D7DC0075D8BD /* Sparkle */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 788687FF2DFF4FCB00B22C15 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 788687E92DFF4FCB00B22C15 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 788687F02DFF4FCB00B22C15; + remoteInfo = VibeTunnel; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 788687F12DFF4FCB00B22C15 /* VibeTunnel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VibeTunnel.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 788687FE2DFF4FCB00B22C15 /* VibeTunnelTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VibeTunnelTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 78868B612DFF808300B22C15 /* Exceptions for "VibeTunnel" folder in "VibeTunnel" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + Shared.xcconfig, + version.xcconfig, + ); + target = 788687F02DFF4FCB00B22C15 /* VibeTunnel */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 788687F32DFF4FCB00B22C15 /* VibeTunnel */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 78868B612DFF808300B22C15 /* Exceptions for "VibeTunnel" folder in "VibeTunnel" target */, + ); + path = VibeTunnel; + sourceTree = ""; + }; + 788688012DFF4FCB00B22C15 /* VibeTunnelTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = VibeTunnelTests; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 788687EE2DFF4FCB00B22C15 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 78AD8B952E051ED40009725C /* Logging in Frameworks */, + 89D01D862CB5D7DC0075D8BD /* Sparkle in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 788687FB2DFF4FCB00B22C15 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 788687E82DFF4FCB00B22C15 = { + isa = PBXGroup; + children = ( + 788687F32DFF4FCB00B22C15 /* VibeTunnel */, + 788688012DFF4FCB00B22C15 /* VibeTunnelTests */, + 78AD8B8F2E051ED40009725C /* Frameworks */, + 788687F22DFF4FCB00B22C15 /* Products */, + ); + sourceTree = ""; + }; + 788687F22DFF4FCB00B22C15 /* Products */ = { + isa = PBXGroup; + children = ( + 788687F12DFF4FCB00B22C15 /* VibeTunnel.app */, + 788687FE2DFF4FCB00B22C15 /* VibeTunnelTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 78AD8B8F2E051ED40009725C /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 788687F02DFF4FCB00B22C15 /* VibeTunnel */ = { + isa = PBXNativeTarget; + buildConfigurationList = 788688152DFF4FCC00B22C15 /* Build configuration list for PBXNativeTarget "VibeTunnel" */; + buildPhases = ( + C3D4E5F6A7B8C9D0E1F23456 /* Install Build Dependencies */, + 788687ED2DFF4FCB00B22C15 /* Sources */, + 788687EE2DFF4FCB00B22C15 /* Frameworks */, + 788687EF2DFF4FCB00B22C15 /* Resources */, + B2C3D4E5F6A7B8C9D0E1F234 /* Build Web Frontend */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 788687F32DFF4FCB00B22C15 /* VibeTunnel */, + ); + name = VibeTunnel; + packageProductDependencies = ( + 89D01D852CB5D7DC0075D8BD /* Sparkle */, + 78AD8B942E051ED40009725C /* Logging */, + ); + productName = VibeTunnel; + productReference = 788687F12DFF4FCB00B22C15 /* VibeTunnel.app */; + productType = "com.apple.product-type.application"; + }; + 788687FD2DFF4FCB00B22C15 /* VibeTunnelTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 788688182DFF4FCC00B22C15 /* Build configuration list for PBXNativeTarget "VibeTunnelTests" */; + buildPhases = ( + 788687FA2DFF4FCB00B22C15 /* Sources */, + 788687FB2DFF4FCB00B22C15 /* Frameworks */, + 788687FC2DFF4FCB00B22C15 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 788688002DFF4FCB00B22C15 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 788688012DFF4FCB00B22C15 /* VibeTunnelTests */, + ); + name = VibeTunnelTests; + productName = VibeTunnelTests; + productReference = 788687FE2DFF4FCB00B22C15 /* VibeTunnelTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 788687E92DFF4FCB00B22C15 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1610; + LastUpgradeCheck = 2600; + TargetAttributes = { + 788687F02DFF4FCB00B22C15 = { + CreatedOnToolsVersion = 16.1; + }; + 788687FD2DFF4FCB00B22C15 = { + CreatedOnToolsVersion = 16.1; + TestTargetID = 788687F02DFF4FCB00B22C15; + }; + }; + }; + buildConfigurationList = 788687EC2DFF4FCB00B22C15 /* Build configuration list for PBXProject "VibeTunnel" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 788687E82DFF4FCB00B22C15; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 89D01D842CB5D7DC0075D8BD /* XCRemoteSwiftPackageReference "Sparkle" */, + 78AD8B8E2E051EB50009725C /* XCRemoteSwiftPackageReference "swift-log" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 788687F22DFF4FCB00B22C15 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 788687F02DFF4FCB00B22C15 /* VibeTunnel */, + 788687FD2DFF4FCB00B22C15 /* VibeTunnelTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 788687EF2DFF4FCB00B22C15 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 788687FC2DFF4FCB00B22C15 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + B2C3D4E5F6A7B8C9D0E1F234 /* Build Web Frontend */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Build Web Frontend"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(CONTENTS_FOLDER_PATH)/Resources/web/public", + "$(BUILT_PRODUCTS_DIR)/$(CONTENTS_FOLDER_PATH)/Resources/vibetunnel", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/zsh; + shellScript = "# Build web frontend using Bun\necho \"Building web frontend...\"\n\n# Run the build script\n\"${SRCROOT}/scripts/build-web-frontend.sh\"\n\nif [ $? -ne 0 ]; then\n echo \"error: Web frontend build failed\"\n exit 1\nfi\n"; + }; + C3D4E5F6A7B8C9D0E1F23456 /* Install Build Dependencies */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Install Build Dependencies"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/zsh; + shellScript = "# Install build dependencies\necho \"Checking build dependencies...\"\n\n# Run the install script\n\"${SRCROOT}/scripts/install-bun.sh\"\n\nif [ $? -ne 0 ]; then\n echo \"error: Failed to install build dependencies\"\n exit 1\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 788687ED2DFF4FCB00B22C15 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 788687FA2DFF4FCB00B22C15 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 788688002DFF4FCB00B22C15 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 788687F02DFF4FCB00B22C15 /* VibeTunnel */; + targetProxy = 788687FF2DFF4FCB00B22C15 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 788688102DFF4FCC00B22C15 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 788687F32DFF4FCB00B22C15 /* VibeTunnel */; + baseConfigurationReferenceRelativePath = Shared.xcconfig; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = complete; + SWIFT_VERSION = 6.0; + }; + name = Debug; + }; + 788688112DFF4FCC00B22C15 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 788687F32DFF4FCB00B22C15 /* VibeTunnel */; + baseConfigurationReferenceRelativePath = Shared.xcconfig; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_STRICT_CONCURRENCY = complete; + SWIFT_VERSION = 6.0; + }; + name = Release; + }; + 788688132DFF4FCC00B22C15 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; + CODE_SIGN_ENTITLEMENTS = VibeTunnel/VibeTunnel.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = "$(inherited)"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = 7F5Y92G2Z4; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = VibeTunnel/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = VibeTunnel; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; + INFOPLIST_KEY_LSUIElement = YES; + INFOPLIST_KEY_NSAppleEventsUsageDescription = "VibeTunnel uses AppleScript to spawn a terminal when you create a new session in the dashboard. This allows VibeTunnel to automatically open your preferred terminal application and connect it to the remote session."; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 VibeTunnel Team. All rights reserved."; + INFOPLIST_KEY_NSMainStoryboardFile = Main; + INFOPLIST_KEY_NSPrincipalClass = NSApplication; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.vibetunnel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + }; + name = Debug; + }; + 788688142DFF4FCC00B22C15 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; + CODE_SIGN_ENTITLEMENTS = VibeTunnel/VibeTunnel.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = "$(inherited)"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = 7F5Y92G2Z4; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = VibeTunnel/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = VibeTunnel; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; + INFOPLIST_KEY_LSUIElement = YES; + INFOPLIST_KEY_NSAppleEventsUsageDescription = "VibeTunnel uses AppleScript to spawn a terminal when you create a new session in the dashboard. This allows VibeTunnel to automatically open your preferred terminal application and connect it to the remote session."; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 VibeTunnel Team. All rights reserved."; + INFOPLIST_KEY_NSMainStoryboardFile = Main; + INFOPLIST_KEY_NSPrincipalClass = NSApplication; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.vibetunnel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + }; + name = Release; + }; + 788688162DFF4FCC00B22C15 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = "$(inherited)"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.vibetunnelTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VibeTunnel.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VibeTunnel"; + }; + name = Debug; + }; + 788688172DFF4FCC00B22C15 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = "$(inherited)"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.vibetunnelTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VibeTunnel.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VibeTunnel"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 788687EC2DFF4FCB00B22C15 /* Build configuration list for PBXProject "VibeTunnel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 788688102DFF4FCC00B22C15 /* Debug */, + 788688112DFF4FCC00B22C15 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 788688152DFF4FCC00B22C15 /* Build configuration list for PBXNativeTarget "VibeTunnel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 788688132DFF4FCC00B22C15 /* Debug */, + 788688142DFF4FCC00B22C15 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 788688182DFF4FCC00B22C15 /* Build configuration list for PBXNativeTarget "VibeTunnelTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 788688162DFF4FCC00B22C15 /* Debug */, + 788688172DFF4FCC00B22C15 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 78AD8B8E2E051EB50009725C /* XCRemoteSwiftPackageReference "swift-log" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-log.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.6.3; + }; + }; + 89D01D842CB5D7DC0075D8BD /* XCRemoteSwiftPackageReference "Sparkle" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sparkle-project/Sparkle"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.7.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78AD8B942E051ED40009725C /* Logging */ = { + isa = XCSwiftPackageProductDependency; + package = 78AD8B8E2E051EB50009725C /* XCRemoteSwiftPackageReference "swift-log" */; + productName = Logging; + }; + 89D01D852CB5D7DC0075D8BD /* Sparkle */ = { + isa = XCSwiftPackageProductDependency; + package = 89D01D842CB5D7DC0075D8BD /* XCRemoteSwiftPackageReference "Sparkle" */; + productName = Sparkle; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 788687E92DFF4FCB00B22C15 /* Project object */; +} diff --git a/mac/scripts/build-web-frontend-conditional.sh b/mac/scripts/build-web-frontend-conditional.sh new file mode 100755 index 00000000..1d7e3582 --- /dev/null +++ b/mac/scripts/build-web-frontend-conditional.sh @@ -0,0 +1,145 @@ +#!/bin/zsh +set -e # Exit on any error + +# Get the project directory +if [ -z "${SRCROOT}" ]; then + # If SRCROOT is not set (running outside Xcode), determine it from script location + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +else + PROJECT_DIR="${SRCROOT}" +fi + +WEB_DIR="${PROJECT_DIR}/../web" +HASH_FILE="${BUILT_PRODUCTS_DIR}/.web-content-hash" +PREVIOUS_HASH_FILE="${BUILT_PRODUCTS_DIR}/.web-content-hash.previous" +PUBLIC_DIR="${WEB_DIR}/public" + +# Set destination directory +if [ -z "${BUILT_PRODUCTS_DIR}" ]; then + # Default for testing outside Xcode + DEST_DIR="/tmp/vibetunnel-web-build" +else + DEST_DIR="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Resources/web/public" +fi + +APP_RESOURCES="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +# Read the current hash +if [ -f "${HASH_FILE}" ]; then + CURRENT_HASH=$(cat "${HASH_FILE}") +else + echo "error: Hash file not found. Run 'Calculate Web Hash' build phase first." + exit 1 +fi + +# Check if we need to rebuild +NEED_REBUILD=1 + +# Check if previous hash exists and matches current +if [ -f "${PREVIOUS_HASH_FILE}" ]; then + PREVIOUS_HASH=$(cat "${PREVIOUS_HASH_FILE}") + if [ "${CURRENT_HASH}" = "${PREVIOUS_HASH}" ]; then + # Also check if the built files actually exist + if [ -d "${DEST_DIR}" ] && [ -f "${APP_RESOURCES}/vibetunnel" ] && [ -f "${APP_RESOURCES}/pty.node" ] && [ -f "${APP_RESOURCES}/spawn-helper" ]; then + echo "Web content unchanged and build outputs exist. Skipping rebuild." + NEED_REBUILD=0 + else + echo "Web content unchanged but build outputs missing. Rebuilding..." + fi + else + echo "Web content changed. Hash: ${PREVIOUS_HASH} -> ${CURRENT_HASH}" + fi +else + echo "No previous build hash found. Building web frontend..." +fi + +if [ ${NEED_REBUILD} -eq 0 ]; then + echo "Skipping web frontend build (no changes detected)" + exit 0 +fi + +echo "Building web frontend..." + +# Setup PATH for Node.js +export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH" + +# Load NVM if available +if [ -s "$HOME/.nvm/nvm.sh" ]; then + export NVM_DIR="$HOME/.nvm" + . "$NVM_DIR/nvm.sh" +fi + +# Export CI to prevent interactive prompts +export CI=true + +# Check if npm is available +if ! command -v npm &> /dev/null; then + echo "error: npm not found. Please install Node.js" + exit 1 +fi + +echo "Using npm version: $(npm --version)" +echo "Using Node.js version: $(node --version)" + +# Check if web directory exists +if [ ! -d "${WEB_DIR}" ]; then + echo "error: Web directory not found at ${WEB_DIR}" + exit 1 +fi + +# Change to web directory +cd "${WEB_DIR}" + +# Clean build artifacts +echo "Cleaning build artifacts..." +rm -rf dist public/bundle public/output.css + +# Install dependencies +echo "Installing dependencies..." +npm install + +# Build the web frontend +echo "Building web frontend..." +npm run build + +# Clean and create destination directory +echo "Cleaning destination directory..." +rm -rf "${DEST_DIR}" +mkdir -p "${DEST_DIR}" + +# Copy built files to Resources +echo "Copying web files to app bundle..." +cp -R "${PUBLIC_DIR}/"* "${DEST_DIR}/" + +# Copy native executable and modules to app bundle root +echo "Copying native executable and modules..." +NATIVE_DIR="${WEB_DIR}/native" + +if [ -f "${NATIVE_DIR}/vibetunnel" ]; then + cp "${NATIVE_DIR}/vibetunnel" "${APP_RESOURCES}/" + chmod +x "${APP_RESOURCES}/vibetunnel" +else + echo "error: vibetunnel executable not found" + exit 1 +fi + +if [ -f "${NATIVE_DIR}/pty.node" ]; then + cp "${NATIVE_DIR}/pty.node" "${APP_RESOURCES}/" +else + echo "error: pty.node not found" + exit 1 +fi + +if [ -f "${NATIVE_DIR}/spawn-helper" ]; then + cp "${NATIVE_DIR}/spawn-helper" "${APP_RESOURCES}/" + chmod +x "${APP_RESOURCES}/spawn-helper" +else + echo "error: spawn-helper not found" + exit 1 +fi + +# Save the current hash as the previous hash for next build +cp "${HASH_FILE}" "${PREVIOUS_HASH_FILE}" + +echo "Web frontend build completed successfully" \ No newline at end of file diff --git a/mac/scripts/calculate-web-hash.sh b/mac/scripts/calculate-web-hash.sh new file mode 100755 index 00000000..a02c8cd9 --- /dev/null +++ b/mac/scripts/calculate-web-hash.sh @@ -0,0 +1,55 @@ +#!/bin/zsh +set -e # Exit on any error + +# Get the project directory +if [ -z "${SRCROOT}" ]; then + # If SRCROOT is not set (running outside Xcode), determine it from script location + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +else + PROJECT_DIR="${SRCROOT}" +fi + +WEB_DIR="${PROJECT_DIR}/../web" +HASH_FILE="${BUILT_PRODUCTS_DIR}/.web-content-hash" + +# Check if web directory exists +if [ ! -d "${WEB_DIR}" ]; then + echo "error: Web directory not found at ${WEB_DIR}" + exit 1 +fi + +# Calculate hash of all relevant SOURCE files in web directory +# Include: src/, scripts/, config files (but NOT package-lock.json) +# Exclude: node_modules, dist, public (all are build outputs) +echo "Calculating web content hash..." +cd "${WEB_DIR}" + +# Find all relevant files and calculate their size, modification time, and content hash +# This approach is more reliable than just content hash as it catches permission changes +# Exclude: node_modules, dist, public (all build outputs), package-lock.json +CONTENT_HASH=$(find . \ + -type f \ + \( -name "*.ts" -o -name "*.js" -o -name "*.json" -o -name "*.css" -o -name "*.html" \ + -o -name "*.tsx" -o -name "*.jsx" -o -name "*.vue" -o -name "*.svelte" \ + -o -name "*.yaml" -o -name "*.yml" -o -name "*.toml" \) \ + -not -path "./node_modules/*" \ + -not -path "./dist/*" \ + -not -path "./public/*" \ + -not -path "./.next/*" \ + -not -path "./coverage/*" \ + -not -path "./.cache/*" \ + -not -name "package-lock.json" \ + -exec stat -f "%m %z %p" {} \; \ + -exec shasum -a 256 {} \; | \ + sort | \ + shasum -a 256 | \ + cut -d' ' -f1) + +echo "Web content hash: ${CONTENT_HASH}" + +# Create directory for hash file if it doesn't exist +mkdir -p "$(dirname "${HASH_FILE}")" + +# Write the hash to file +echo "${CONTENT_HASH}" > "${HASH_FILE}" \ No newline at end of file