-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve a flow of loading devices and xcode detection #563
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
@@ -428,9 +425,12 @@ export class DependencyManager implements Disposable { | |||
export async function checkIfCLIInstalled(cmd: string, options: Record<string, unknown> = {}) { | |||
try { | |||
// We are not checking the stderr here, because some of the CLIs put the warnings there. | |||
const { stdout } = await command(cmd, { encoding: "utf8", ...options }); | |||
return !!stdout.length; | |||
const { stdout } = await command(cmd, { encoding: "utf8", quiet: true, ...options }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
quiet
name needs to change eventually to something like printStdErr
, I'd assume if quiet is true we only get status code on exit.
const { stdout } = await command(cmd, { encoding: "utf8", ...options }); | ||
return !!stdout.length; | ||
const { stdout } = await command(cmd, { encoding: "utf8", quiet: true, ...options }); | ||
const result = !!stdout.length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please be more explicit:
const hasOutput = stdout.lenght > 0;
return !!stdout.length; | ||
const { stdout } = await command(cmd, { encoding: "utf8", quiet: true, ...options }); | ||
const result = !!stdout.length; | ||
Logger.debug(`CLI: ${cmd} ${result ? "" : "not"} installed `); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: "CLI: " prefix is unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is useful to have prefixes that allow to serach what part of our code generated the log. We have. a growing code base and most of that log is generated dynamically so searching for it would be hard without it.
@@ -125,11 +128,11 @@ export class DeviceManager implements DeviceManagerInterface { | |||
return getAvailableIosRuntimes(); | |||
} | |||
|
|||
public async listAllDevices() { | |||
public async listAllDevices(force?: boolean) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe forceReload
?
{devicesNotFound ? <DevicesNotFoundView /> : <VSCodeProgressRing />} | ||
</div> | ||
<LoadingComponent | ||
finishedInitialLoad={finishedInitialLoad} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we're checking for finishedInitialLoad twice in the ternary and in the component?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is more then one reason for rendering the loading component, but not all of them behave the same.
}) | ||
: Promise.resolve([]); | ||
|
||
const shouldLoadSimulators = Platform.OS === "macos" && (await checkXcodeExists()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can add additional log here when checkXcodeExists fails. For example "Couldn't list iOS simulators as XCode installation wasn't found" – this will allow us to track issues in case those checks aren't accurate
@@ -26,6 +26,7 @@ const ProjectContext = createContext<ProjectContextProps>({ | |||
longitude: 19.965474, | |||
isDisabled: false, | |||
}, | |||
locale: "en_US", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this change here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was missed before and ts was not happy, it does not effect the webview much at all as it never uses default context.
const devices = extensionContext.globalState.get(DEVICE_LIST_CACHE_KEY) as | ||
| DeviceInfo[] | ||
| undefined; | ||
if (devices) { | ||
if (devices && !forceReload) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this isn't accurate as we always load the devices. The difference is that we return the updated list or let it update asynchronously. I'm a bit hesitant about this change as from what I remember, we added device caching as it was slowing down the initial startup significantly. As it isn't documented in PR description I wonder why you're changing this behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When user had an iPhone selected when they last used IDE, but something happend to xcode (e.g. user started using beta but not updated the xcode-select
) application would select an iPhone anyway as it is the most recent device and would get stuck on loading screen.
What dou you think about forceLoad
instead of forceReload
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should optimize for the most common case in which people don't change their xcode location. If there are cases when it happens, I understand they will get an error when trying to load the simulator, and by that time we will have the list refetched. If that's not the case we should adjust the logic such that the worst thing that can happen if you mess up the simjulator cache or xcode is that you get an error loading the simulator instead of slowing down all the time to interact for everyone else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks mostly good, left a few comments inline
|
||
let shouldLoadSimulators = Platform.OS === "macos"; | ||
|
||
if (!(await checkXcodeExists())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we even want to run those checks if we're not on a Mac?
Perhaps doing it this way would be cleaner:
const shouldLoadSimulators = Platform.OS === "macos" && (await checkXcodeExists());
Then you can move the log to be printed in checkXcodeExists
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did this in this way to address your previous comment about adding logging in !checkXcodeExist() scennario
const selectInitialDevicePromise = selectInitialDevice(devices); | ||
|
||
const listener = async (newDevices: DeviceInfo[]) => { | ||
if (await selectInitialDevicePromise) { | ||
this.deviceManager.removeListener("devicesChanged", listener); | ||
return; | ||
} | ||
if (await selectInitialDevice(newDevices)) { | ||
this.deviceManager.removeListener("devicesChanged", listener); | ||
} | ||
}; | ||
this.deviceManager.addListener("devicesChanged", listener); | ||
|
||
if (await selectInitialDevicePromise) { | ||
this.deviceManager.removeListener("devicesChanged", listener); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code doesn't do the same thing as the one on the left. In general I'm most afraid of the changes in awaiting that you're adding here. Not saying we shouldn't have them, but they require some explanation in the PR description, such that we can understand why we're changing the await flow.
This part is the most complex change and it introduces some unusual pattern (that we, albeit, use in other places but typically with some explanation) where we create promise and then await inside of an arrow function.
I think that given this method already awaits selectDevice
(which wasn't the case before this change) , we can just add the listener before calling selectInitialDevice
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm 👍
This PR fixes a mistake introduced in #563, the return value of `selectDevice` should indicate if device was chosen successfully, not if processes on it are successful otherwise if build fails and the selected device was set by `trySelectingInitiallDevice` we are getting stuck in infinite loop.
this PR addresses a few issues with the loading devices flow:
checkIfCLIInstalled
no longer logs errors when returning false instead just logs with debug level, this change makes the logs more alined with expected results for setups without xcodeDYLD_FRAMEWORK_PATH
as it is no longer neededtrySelectingInitialDevice()
creates "devicesChanged" listeners before selectingInitial device is complete, so it catches cases in whichlastDeviceId
points to the device that no longer exists (e.g. because of xcode update)