// Get rid of any zombie lldb/iosdeploy processes, this needs to be reworked to use tracked process id's when running parallel tests across multiple AutomationTool.exe processes on test workers void KillZombies() { if (Globals.IsWorker || ZombiesKilled) { return; } ZombiesKilled = true; IOSBuild.ExecuteCommand("killall", "ios-deploy"); Thread.Sleep(2500); IOSBuild.ExecuteCommand("killall", "lldb"); Thread.Sleep(2500); }
public bool Reboot() { const string Cmd = "/usr/local/bin/idevicediagnostics"; if (!File.Exists(Cmd)) { Log.Verbose("Rebooting iOS device requires idevicediagnostics binary"); return(true); } var Result = IOSBuild.ExecuteCommand(Cmd, string.Format("restart -u {0}", DeviceName)); if (Result.ExitCode != 0) { Log.Warning(string.Format("Failed to reboot iOS device {0}, restart command failed", DeviceName)); return(true); } // initial wait 20 seconds Thread.Sleep(20 * 1000); const int WaitPeriod = 10; int WaitTime = 120; bool rebooted = false; do { Result = IOSBuild.ExecuteCommand(Cmd, string.Format("diagnostics WiFi -u {0}", DeviceName)); if (Result.ExitCode == 0) { rebooted = true; break; } Thread.Sleep(WaitPeriod * 1000); WaitTime -= WaitPeriod; } while (WaitTime > 0); if (!rebooted) { Log.Warning("Failed to reboot iOS device {0}, device didn't come back after restart", DeviceName); } return(true); }
/// <summary> /// Resign application using local executable and update debug symbols /// </summary> void ResignApplication(UnrealAppConfig AppConfig) { // check that we have the signing stuff we need string SignProvision = Globals.Params.ParseValue("signprovision", String.Empty); string SignEntitlements = Globals.Params.ParseValue("signentitlements", String.Empty); string SigningIdentity = Globals.Params.ParseValue("signidentity", String.Empty); // handle signing provision if (string.IsNullOrEmpty(SignProvision) || !File.Exists(SignProvision)) { throw new AutomationException("Absolute path to existing provision must be specified, example: -signprovision=/path/to/myapp.provision"); } // handle entitlements // Note this extracts entitlements: which may be useful when using same provision/entitlements?: codesign -d --entitlements :entitlements.plist ~/.gauntletappcache/Payload/Example.app/ if (string.IsNullOrEmpty(SignEntitlements) || !File.Exists(SignEntitlements)) { throw new AutomationException("Absolute path to existing entitlements must be specified, example: -signprovision=/path/to/entitlements.plist"); } // signing identity if (string.IsNullOrEmpty(SigningIdentity)) { throw new AutomationException("Signing identity must be specified, example: -signidentity=\"iPhone Developer: John Smith\""); } string ProjectName = AppConfig.ProjectName; string BundleName = Path.GetFileNameWithoutExtension(LocalAppBundle); string ExecutableName = UnrealHelpers.GetExecutableName(ProjectName, UnrealTargetPlatform.IOS, AppConfig.Configuration, AppConfig.ProcessType, ""); string CachedAppPath = Path.Combine(GauntletAppCache, "Payload", string.Format("{0}.app", BundleName)); string LocalExecutable = Path.Combine(Environment.CurrentDirectory, ProjectName, string.Format("Binaries/IOS/{0}", ExecutableName)); if (!File.Exists(LocalExecutable)) { throw new AutomationException("Local executable not found for -dev argument: {0}", LocalExecutable); } File.WriteAllText(CacheResignedFilename, "The application has been resigned"); // copy local executable FileInfo SrcInfo = new FileInfo(LocalExecutable); string DestPath = Path.Combine(CachedAppPath, BundleName); SrcInfo.CopyTo(DestPath, true); Log.Verbose("Copied local executable from {0} to {1}", LocalExecutable, DestPath); // copy provision SrcInfo = new FileInfo(SignProvision); DestPath = Path.Combine(CachedAppPath, "embedded.mobileprovision"); SrcInfo.CopyTo(DestPath, true); Log.Verbose("Copied provision from {0} to {1}", SignProvision, DestPath); // handle symbols string LocalSymbolsDir = Path.Combine(Environment.CurrentDirectory, ProjectName, string.Format("Binaries/IOS/{0}.dSYM", ExecutableName)); DestPath = Path.Combine(GauntletAppCache, string.Format("Symbols/{0}.dSYM", ExecutableName)); if (Directory.Exists(DestPath)) { Directory.Delete(DestPath, true); } if (Directory.Exists(LocalSymbolsDir)) { CommandUtils.CopyDirectory_NoExceptions(LocalSymbolsDir, DestPath, true); } else { Log.Warning("No symbols found for local build at {0}, removing cached app symbols", LocalSymbolsDir); } // resign application // @todo: this asks for password unless "Always Allow" is selected, also for builders, document how to permanently grant codesign access to keychain string SignArgs = string.Format("-f -s \"{0}\" --entitlements \"{1}\" \"{2}\"", SigningIdentity, SignEntitlements, CachedAppPath); Log.Info("\nResigning app, please enter keychain password if prompted:\n\ncodesign {0}", SignArgs); var Result = IOSBuild.ExecuteCommand("codesign", SignArgs); if (Result.ExitCode != 0) { throw new AutomationException("Failed to resign application"); } }
/// <summary> /// Generate MD5 and cache IPA bundle files /// </summary> private bool PrepareIPA(IOSBuild Build) { Log.Info("Preparing IPA {0}", Build.SourceIPAPath); try { // cache the unzipped app using a MD5 checksum, avoiding needing to unzip string Hash = null; string StoredHash = null; using (var MD5Hash = MD5.Create()) { using (var Stream = File.OpenRead(Build.SourceIPAPath)) { Hash = BitConverter.ToString(MD5Hash.ComputeHash(Stream)).Replace("-", "").ToLowerInvariant(); } } string PayloadDir = Path.Combine(GauntletAppCache, "Payload"); string SymbolsDir = Path.Combine(GauntletAppCache, "Symbols"); if (File.Exists(IPAHashFilename) && Directory.Exists(PayloadDir)) { StoredHash = File.ReadAllText(IPAHashFilename).Trim(); if (Hash != StoredHash) { Log.Verbose("IPA hash out of date, clearing cache"); StoredHash = null; } } if (String.IsNullOrEmpty(StoredHash) || Hash != StoredHash) { if (Directory.Exists(PayloadDir)) { Directory.Delete(PayloadDir, true); } if (Directory.Exists(SymbolsDir)) { Directory.Delete(SymbolsDir, true); } if (File.Exists(CacheResignedFilename)) { File.Delete(CacheResignedFilename); } Log.Verbose("Unzipping IPA {0} to cache at: {1}", Build.SourceIPAPath, GauntletAppCache); IProcessResult Result = IOSBuild.ExecuteCommand("unzip", String.Format("{0} -d {1}", Build.SourceIPAPath, GauntletAppCache)); if (Result.ExitCode != 0 || !Directory.Exists(PayloadDir)) { throw new Exception(String.Format("Unable to extract IPA {0}", Build.SourceIPAPath)); } // Cache symbols for symbolicated callstacks string SymbolsZipFile = string.Format("{0}/../../Symbols/{1}.dSYM.zip", Path.GetDirectoryName(Build.SourceIPAPath), Path.GetFileNameWithoutExtension(Build.SourceIPAPath)); Log.Verbose("Checking Symbols at {0}", SymbolsZipFile); if (File.Exists(SymbolsZipFile)) { Log.Verbose("Unzipping Symbols {0} to cache at: {1}", SymbolsZipFile, SymbolsDir); Result = IOSBuild.ExecuteCommand("unzip", String.Format("{0} -d {1}", SymbolsZipFile, SymbolsDir)); if (Result.ExitCode != 0 || !Directory.Exists(SymbolsDir)) { throw new Exception(String.Format("Unable to extract build symbols {0} -> {1}", SymbolsZipFile, SymbolsDir)); } } // store hash File.WriteAllText(IPAHashFilename, Hash); Log.Verbose("IPA cached"); } else { Log.Verbose("Using cached IPA"); } LocalAppBundle = Directory.GetDirectories(PayloadDir).Where(D => Path.GetExtension(D) == ".app").FirstOrDefault(); if (String.IsNullOrEmpty(LocalAppBundle)) { throw new Exception(String.Format("Unable to find app in local app bundle {0}", PayloadDir)); } } catch (Exception Ex) { throw new AutomationException("Unable to prepare {0} : {1}", Build.SourceIPAPath, Ex.Message); } return(true); }