Skip to content

Commit

Permalink
feat!: improved xcodebuild performance by blocking Apple domain
Browse files Browse the repository at this point in the history
BREAKING CHANGE: (optional) please see `:h
xcodebuild.xcodebuild-offline` to enable tool that improves build speed.

More details about this issue here (#201):
#201 (comment)
  • Loading branch information
wojciech-kulik committed Oct 20, 2024
1 parent 3b3cea5 commit 25dbdd3
Show file tree
Hide file tree
Showing 13 changed files with 349 additions and 76 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,7 @@ return {
Install external tools:

```shell
brew install xcode-build-server
brew install xcbeautify
brew install ruby
brew install pipx
brew install xcode-build-server xcbeautify ruby pipx
gem install xcodeproj
pipx install pymobiledevice3
```
Expand Down Expand Up @@ -335,6 +332,8 @@ vim.keymap.set("n", "<leader>xa", "<cmd>XcodebuildCodeActions<cr>", { desc = "Sh
remote_debugger = nil, -- optional path to local copy of remote_debugger (check out README for details)
remote_debugger_port = 65123, -- port used by remote debugger (passed to pymobiledevice3)
focus_simulator_on_app_launch = true, -- focus simulator window when app is launched
run_xcodebuild_in_offline_mode = false, -- significantly speeds up xcodebuild by disabling network access (requires configuration, see `:h xcodebuild.xcodebuild-offline`)
xcodebuild_offline = nil, -- optional path to local copy of xcodebuild-offline (see `:h xcodebuild.xcodebuild-offline`)
},
logs = { -- build & test logs
auto_open_on_success_tests = false, -- open logs when tests succeeded
Expand Down
85 changes: 81 additions & 4 deletions doc/xcodebuild.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ neo-tree.nvim Integration ··················· |xcodebuild.int
oil.nvim Integration ························ |xcodebuild.integrations.oil-nvim|
Quick Test Framework Integration ··············· |xcodebuild.integrations.quick|
xcode-build-server Integration ···· |xcodebuild.integrations.xcode-build-server|
Xcodebuild Workaround ············· |xcodebuild.integrations.xcodebuild-offline|
Pickers ················································ |xcodebuild.ui.pickers|
Helpers ··················································· |xcodebuild.helpers|
Lua Utils ···················································· |xcodebuild.util|
Expand Down Expand Up @@ -92,6 +93,8 @@ M.setup({options}) *xcodebuild.setup*
remote_debugger = nil, -- optional path to local copy of remote_debugger (check out README for details)
remote_debugger_port = 65123, -- port used by remote debugger (passed to pymobiledevice3)
focus_simulator_on_app_launch = true, -- focus simulator window when app is launched
run_xcodebuild_in_offline_mode = false, -- significantly speeds up xcodebuild by disabling network access (requires configuration, see `:h xcodebuild.xcodebuild-offline`)
xcodebuild_offline = nil, -- optional path to local copy of xcodebuild-offline (see `:h xcodebuild.xcodebuild-offline`)
},
logs = { -- build & test logs
auto_open_on_success_tests = false, -- open logs when tests succeeded
Expand Down Expand Up @@ -247,7 +250,7 @@ Availability of features


🔐 - requires passwordless `sudo` permission to `tools/remote_debugger`
script (see |xcodebuild.sudo|).
script (see |xcodebuild.ios17|).

🛠️ - available if pymobiledevice3 is installed.

Expand Down Expand Up @@ -290,7 +293,7 @@ or just:
<

To debug on physical devices with iOS 17+ you will need to set up `sudo`,
see |xcodebuild.sudo| to learn more.
see |xcodebuild.ios17| to learn more.


==============================================================================
Expand Down Expand Up @@ -543,8 +546,7 @@ Sample `lualine` integration:
==============================================================================
Debugging On iOS 17+ Device *xcodebuild.integrations.ios17*

*xcodebuild.integrations.sudo*
*xcodebuild.sudo*
*xcodebuild.ios17*
Since iOS 17, a new secure connection between Mac and mobile devices is
required. Xcodebuild.nvim uses `pymobiledevice3` to establish a special
trusted tunnel that is later used for debugging. However, this operation
Expand Down Expand Up @@ -1705,6 +1707,7 @@ AppData *xcodebuild.project.appdata.AppData*
{GETSNAPSHOTS_TOOL} (string) # The name of the getsnapshots tool.
{PROJECT_HELPER_TOOL} (string) # The name of the project helper tool.
{REMOTE_DEBUGGER_TOOL} (string) # The name of the remote debugger tool.
{XCODEBUILD_OFFLINE_TOOL} (string) # The name of the xcodebuild offline tool.


M.tool_path({name}) *xcodebuild.project.appdata.tool_path*
Expand Down Expand Up @@ -3699,6 +3702,80 @@ M.run_config({projectCommand}, {scheme})
(number) job id


==============================================================================
Xcodebuild Workaround *xcodebuild.integrations.xcodebuild-offline*

*xcodebuild.xcodebuild-offline*

This module provides a workaround for the issue with slow `xcodebuild` command.

The issue is caused by the fact that `xcodebuild` tries to connect to the Apple
servers before building the project, which can take 20 seconds or more.
Usually, those requests are not necessary, but they slow down each build.

This module provides a workaround by mapping Apple servers to localhost in the
`/etc/hosts` file during the build. It is a temporary solution and should be
used with caution.

Keep in mind that disabling access to `developerservices2.apple.com` for
`xcodebuild` may cause some issues with the build process. It will disable
things like registering devices, capabilities, and other network-related
features. Therefore, it's best to use it when you are working just on the
code and don't need updating project settings.

To enable this workaround, set `commands.run_xcodebuild_in_offline_mode`
to `true` in the configuration.

👉 Passwordless access to `xcodebuild_offline`

This workaround requires `sudo` to update the `/etc/hosts` file.
Make sure to use the command below, otherwise you may break your `sudo` command:

>bash
sudo visudo -f /etc/sudoers
<

Append this line, but first update the path and the username:

>bash
YOUR_USERNAME ALL = (ALL) NOPASSWD: /Users/YOUR_USERNAME/.local/share/nvim/lazy/xcodebuild.nvim/tools/xcodebuild_offline
<

👉 Creating a local copy of `xcodebuild_offline`

If you don't want to configure the passwordless permission to the file
that could be changed in the future, you can make a local copy of this
script, set your local path in the config `commands.xcodebuild_offline`,
and update `/etc/sudoers` accordingly.

Please remember that you will have to update this file manually if it
changes in the future.



More details about this issue can be found here:
https://github.com/wojciech-kulik/xcodebuild.nvim/issues/201#issuecomment-2423828065


*xcodebuild.integrations.xcodebuild-offline.is_enabled*
M.is_enabled()
Returns whether the `xcodebuild` command should be run in offline mode.

Returns: ~
(boolean)


*xcodebuild.integrations.xcodebuild-offline.wrap_command_if_needed*
M.wrap_command_if_needed({command})
Wraps the `xcodebuild` command with the workaround script if needed.

Parameters: ~
{command} (string)

Returns: ~
(string)


==============================================================================
Pickers *xcodebuild.ui.pickers*

Expand Down
2 changes: 2 additions & 0 deletions lua/xcodebuild/core/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ local defaults = {
remote_debugger = nil, -- optional path to local copy of remote_debugger (check out README for details)
remote_debugger_port = 65123, -- port used by remote debugger (passed to pymobiledevice3)
focus_simulator_on_app_launch = true, -- focus simulator window when app is launched
run_xcodebuild_in_offline_mode = false, -- significantly speeds up xcodebuild by disabling network access (requires configuration, see `:h xcodebuild.xcodebuild-offline`)
xcodebuild_offline = nil, -- optional path to local copy of xcodebuild-offline (see `:h xcodebuild.xcodebuild-offline`)
},
logs = { -- build & test logs
auto_open_on_success_tests = false, -- open logs when tests succeeded
Expand Down
9 changes: 9 additions & 0 deletions lua/xcodebuild/core/xcode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
local util = require("xcodebuild.util")
local notifications = require("xcodebuild.broadcasting.notifications")
local constants = require("xcodebuild.core.constants")
local xcodebuildOffline = require("xcodebuild.integrations.xcodebuild-offline")

local M = {}
local CANCELLED_CODE = 143
Expand Down Expand Up @@ -144,6 +145,7 @@ end
---@return number # job id
function M.get_destinations(projectCommand, scheme, callback)
local command = "xcodebuild -showdestinations " .. projectCommand .. " -scheme '" .. scheme .. "'"
command = xcodebuildOffline.wrap_command_if_needed(command)

return vim.fn.jobstart(command, {
stdout_buffered = true,
Expand Down Expand Up @@ -185,6 +187,7 @@ end
---@return number # job id
function M.get_schemes(projectCommand, callback)
local command = "xcodebuild " .. projectCommand .. " -list"
command = xcodebuildOffline.wrap_command_if_needed(command)

return vim.fn.jobstart(command, {
stdout_buffered = true,
Expand Down Expand Up @@ -254,6 +257,7 @@ end
---@return number # job id
function M.get_project_information(xcodeproj, callback)
local command = "xcodebuild -project '" .. xcodeproj .. "' -list"
command = xcodebuildOffline.wrap_command_if_needed(command)

return vim.fn.jobstart(command, {
stdout_buffered = true,
Expand Down Expand Up @@ -304,6 +308,7 @@ end
---@return number # job id
function M.get_testplans(projectCommand, scheme, callback)
local command = "xcodebuild test " .. projectCommand .. " -scheme '" .. scheme .. "' -showTestPlans"
command = xcodebuildOffline.wrap_command_if_needed(command)

return vim.fn.jobstart(command, {
stdout_buffered = true,
Expand Down Expand Up @@ -346,6 +351,7 @@ function M.build_project(opts)
.. opts.destination
.. "'"
.. (string.len(opts.extraBuildArgs) > 0 and " " .. opts.extraBuildArgs or "")
command = xcodebuildOffline.wrap_command_if_needed(command)

return vim.fn.jobstart(command, {
stdout_buffered = false,
Expand Down Expand Up @@ -393,6 +399,7 @@ function M.get_build_settings(platform, projectCommand, scheme, xcodeprojPath, c
.. "' -showBuildSettings"
.. " -sdk "
.. sdk
command = xcodebuildOffline.wrap_command_if_needed(command)

if config then
command = command .. " -configuration '" .. config .. "'"
Expand Down Expand Up @@ -747,6 +754,7 @@ function M.enumerate_tests(opts, callback)
.. "' -disableAutomaticPackageResolution -skipPackageUpdates -parallelizeTargets"
.. " -test-enumeration-style flat"
.. (string.len(opts.extraTestArgs) > 0 and " " .. opts.extraTestArgs or "")
command = xcodebuildOffline.wrap_command_if_needed(command)

return vim.fn.jobstart(command, {
on_exit = function(_, code, _)
Expand Down Expand Up @@ -807,6 +815,7 @@ function M.run_tests(opts)
.. opts.testPlan
.. "'"
.. (string.len(opts.extraTestArgs) > 0 and " " .. opts.extraTestArgs or "")
command = xcodebuildOffline.wrap_command_if_needed(command)

if opts.testsToRun then
for _, test in ipairs(opts.testsToRun) do
Expand Down
2 changes: 1 addition & 1 deletion lua/xcodebuild/docs/features.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
---
---
--- 🔐 - requires passwordless `sudo` permission to `tools/remote_debugger`
--- script (see |xcodebuild.sudo|).
--- script (see |xcodebuild.ios17|).
---
--- 🛠️ - available if pymobiledevice3 is installed.
---
Expand Down
3 changes: 1 addition & 2 deletions lua/xcodebuild/docs/ios17.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---@mod xcodebuild.integrations.ios17 Debugging On iOS 17+ Device
---@tag xcodebuild.integrations.sudo
---@tag xcodebuild.sudo
---@tag xcodebuild.ios17
---@brief [[
---Since iOS 17, a new secure connection between Mac and mobile devices is
---required. Xcodebuild.nvim uses `pymobiledevice3` to establish a special
Expand Down
2 changes: 1 addition & 1 deletion lua/xcodebuild/docs/requirements.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
---<
---
---To debug on physical devices with iOS 17+ you will need to set up `sudo`,
---see |xcodebuild.sudo| to learn more.
---see |xcodebuild.ios17| to learn more.
---
---@brief ]]

Expand Down
65 changes: 51 additions & 14 deletions lua/xcodebuild/health.lua
Original file line number Diff line number Diff line change
Expand Up @@ -277,31 +277,67 @@ local function check_os()
end
end

local function check_sudo()
local function has_sudo_access(path)
local util = require("xcodebuild.util")
local permissions = util.shell("sudo -l 2>/dev/null")

for _, line in ipairs(permissions) do
if line:match("NOPASSWD.*" .. path) then
return true
end
end

return false
end

local function check_remote_debugger_sudo()
local deviceProxy = require("xcodebuild.platform.device_proxy")
if not deviceProxy.is_installed() then
return
end

start("Checking passwordless sudo")
start("Checking passwordless sudo for remote_debugger")

local config = require("xcodebuild.core.config")
local appdata = require("xcodebuild.project.appdata")
local util = require("xcodebuild.util")

local path = config.options.commands.remote_debugger or appdata.tool_path(appdata.REMOTE_DEBUGGER_TOOL)
local permissions = util.shell("sudo -l 2>/dev/null")
local path = config.options.commands.remote_debugger
and vim.fn.expand(config.options.commands.remote_debugger)
or appdata.tool_path(appdata.REMOTE_DEBUGGER_TOOL)

for _, line in ipairs(permissions) do
if line:match("NOPASSWD.*" .. path) then
ok("sudo: configured")
return
end
if has_sudo_access(path) then
ok("sudo: configured")
else
warn("passwordless sudo permission for `remote_debugger` is not configured.")
warn("debugging on physical devices with iOS 17+ will not work.")
warn("see `:h xcodebuild.ios17` for more information.")
end
end

local function check_xcodebuild_offline_sudo()
local isEnabled = require("xcodebuild.integrations.xcodebuild-offline").is_enabled()
if not isEnabled then
start("Checking xcodebuild_offline tool")
warn("tool not enabled - builds might be slower.")
warn("see `:h xcodebuild.xcodebuild-offline` for more information.")
return
end

start("Checking passwordless sudo for xcodebuild_offline")

warn("sudo: passwordless permission for `remote_debugger` is not configured.")
warn("debugging on physical devices with iOS 17+ will not work.")
warn("see `:h xcodebuild.sudo` for more information.")
local config = require("xcodebuild.core.config")
local appdata = require("xcodebuild.project.appdata")

local path = config.options.commands.xcodebuild_offline
and vim.fn.expand(config.options.commands.xcodebuild_offline)
or appdata.tool_path(appdata.XCODEBUILD_OFFLINE_TOOL)

if has_sudo_access(path) then
ok("sudo: configured")
else
error("passwordless sudo permission for `xcodebuild_offline` is not configured.")
error("see `:h xcodebuild.xcodebuild-offline` for more information.")
end
end

local function check_plugin_commit()
Expand Down Expand Up @@ -367,7 +403,8 @@ M.check = function()
start("Checking .nvim/xcodebuild/settings.json")
check_xcodebuild_settings()

check_sudo()
check_xcodebuild_offline_sudo()
check_remote_debugger_sudo()
end

return M
2 changes: 2 additions & 0 deletions lua/xcodebuild/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ end
--- remote_debugger = nil, -- optional path to local copy of remote_debugger (check out README for details)
--- remote_debugger_port = 65123, -- port used by remote debugger (passed to pymobiledevice3)
--- focus_simulator_on_app_launch = true, -- focus simulator window when app is launched
--- run_xcodebuild_in_offline_mode = false, -- significantly speeds up xcodebuild by disabling network access (requires configuration, see `:h xcodebuild.xcodebuild-offline`)
--- xcodebuild_offline = nil, -- optional path to local copy of xcodebuild-offline (see `:h xcodebuild.xcodebuild-offline`)
--- },
--- logs = { -- build & test logs
--- auto_open_on_success_tests = false, -- open logs when tests succeeded
Expand Down
Loading

0 comments on commit 25dbdd3

Please sign in to comment.