protected async Task Upload(BuildPath zipPath, TaskToken task) { var archive = zipPath.path; if (!File.Exists(archive)) { throw new System.Exception("UploadDistro: Archive file does not exist: " + archive); } // Append a / to the url if necessary, otherwise curl treats the last part as a file name var url = uploadUrl; if (url[url.Length - 1] != '/') { url += "/"; } string input = null; if (!string.IsNullOrEmpty(login.User)) { input = string.Format("-u \"{0}:{1}\"", login.User, login.GetPassword(keychainService)); } var arguments = string.Format( "-T '{0}' {1} --ssl -v '{2}'", archive, input != null ? "-K -" : "", url ); task.Report(0, $"Uploading {Path.GetFileName(archive)} to {uploadUrl}"); await Execute(new ExecutionArgs(curlPath, arguments) { input = input }, task); }
/// <summary> /// Set default values taken from the assigned group. /// </summary> /// <param name="group">The group this schema has been added to.</param> protected override void OnSetGroup(AddressableAssetGroup group) { //this can happen during the load of the addressables asset if (group.Settings != null) { if (BuildPath == null || string.IsNullOrEmpty(BuildPath.GetValue(group.Settings))) { m_BuildPath = new ProfileValueReference(); BuildPath.SetVariableByName(group.Settings, AddressableAssetSettings.kLocalBuildPath); } if (LoadPath == null || string.IsNullOrEmpty(LoadPath.GetValue(group.Settings))) { m_LoadPath = new ProfileValueReference(); LoadPath.SetVariableByName(group.Settings, AddressableAssetSettings.kLocalLoadPath); } } if (m_AssetBundleProviderType.Value == null) { m_AssetBundleProviderType.Value = typeof(AssetBundleProvider); } if (m_BundledAssetProviderType.Value == null) { m_BundledAssetProviderType.Value = typeof(BundledAssetProvider); } }
string ReplaceVariablesIndividual(string input, BuildPath buildPath) { input = input.Replace("{target}", "'" + buildPath.target.ToString() + "'"); input = input.Replace("{path}", "'" + buildPath.path + "'"); input = input.Replace("{project}", "'" + Application.dataPath + "'"); return(input); }
/// <summary> /// Get the paths to all existing builds for all build target in all linked Build Profiles. /// </summary> public virtual IEnumerable <BuildPath> GetBuildPaths() { var paths = new Dictionary <BuildTarget, BuildPath>(); foreach (var profile in builds) { if (profile == null) { continue; } foreach (var target in profile.BuildTargets) { var path = profile.GetLastBuildPath(target); if (string.IsNullOrEmpty(path) || (!File.Exists(path) && !Directory.Exists(path))) { paths[target] = new BuildPath(profile, target, null); } else { paths[target] = new BuildPath(profile, target, path); } } } return(paths.Values); }
public void TestAmountCity() { BuildPath bp = new BuildPath(lines); int expected = 4; Assert.AreEqual(expected, bp.Cities.Count); }
public void TestAmountDirection() { BuildPath bp = new BuildPath(lines); int expected = 3; Assert.AreEqual(expected, bp.Directions.Count); }
public void TestMainMethod() { BuildPath bp = new BuildPath(lines); string expected = "Брест → Москва, Москва → Иркутск, Иркутск → Владивосток"; Assert.AreEqual(expected, bp.result); }
/// <summary> /// Notarize a macOS build. /// </summary> /// <remarks> /// This method will silently ignore non-macOS builds. /// </remarks> /// <param name="buildPath">Build path</param> public async Task NotarizeIfMac(BuildPath buildPath, TaskToken task) { if (buildPath.target == BuildTarget.StandaloneOSX) { var child = task.StartChild("Notarize macOS Build"); try { await Notarize(buildPath, child); } finally { child.Remove(); } } }
public static void RemoveNoNamedBundle(string path) { if (string.IsNullOrEmpty(path)) { return; } var bundleName = path + "/" + BuildPath.GetLastName(path); if (!File.Exists(bundleName)) { return; } var bundle = AssetBundle.LoadFromFile(bundleName); if (bundle == null) { return; } var mainFest = bundle.LoadAsset <AssetBundleManifest>("AssetBundleManifest"); if (mainFest == null) { return; } var oldBundles = mainFest.GetAllAssetBundles(); var newBundles = new HashSet <string>(AssetDatabase.GetAllAssetBundleNames()); foreach (var name in oldBundles) { if (!newBundles.Contains(name)) { var fullName = path + "/" + name; if (File.Exists(fullName)) { File.Delete(fullName); } fullName = path + "/" + name + ".manifest"; if (File.Exists(fullName)) { File.Delete(fullName); } fullName = path + "/" + name + ".meta"; if (File.Exists(fullName)) { File.Delete(fullName); } } } bundle.Unload(true); AssetDatabase.Refresh(); DeleteEmptyFolder(path); AssetDatabase.Refresh(); }
IEnumerator Distribute(BuildPath buildPath) { Debug.Log("ItchDistro: Pushing " + buildPath.target); var path = OptionHelper.GetBuildBasePath(buildPath.path); var channel = ChannelNames[buildPath.target]; if (!string.IsNullOrEmpty(channelSuffix)) { channel += "-" + channelSuffix; } var version = Application.version; var buildInfo = BuildInfo.FromPath(path); if (buildInfo != null) { if (!buildInfo.version.IsDefined) { Debug.LogWarning("ItchDistro: build.json exists but contains no version."); } else { version = buildInfo.version.MajorMinorPatchBuild; } } var args = string.Format( "push '{0}' '{1}:{2}' --userversion '{3}' --ignore='*.DS_Store' --ignore='build.json'", path, project, channel, Application.version ); yield return(Execute(butlerPath, args)); var exitcode = GetSubroutineResult <int>(); yield return(exitcode == 0); }
protected IEnumerator Upload(BuildPath zipPath) { var archive = zipPath.path; if (!File.Exists(archive)) { Debug.LogError("UploadDistro: Archive file does not exist: " + archive); yield return(false); yield break; } // Append a / to the url if necessary, otherwise curl treats the last part as a file name var url = uploadUrl; if (url[url.Length - 1] != '/') { url += "/"; } string input = null; if (!string.IsNullOrEmpty(login.User)) { input = string.Format("-u \"{0}:{1}\"", login.User, login.GetPassword(keychainService)); } var arguments = string.Format( "-T '{0}' {1} --ssl -v '{2}'", archive, input != null ? "-K -" : "", url ); Debug.Log("UploadDistro: Uploading " + Path.GetFileName(archive) + " to " + uploadUrl); yield return(Execute(curlPath, arguments, input)); var exitcode = GetSubroutineResult <int>(); yield return(exitcode == 0); }
async Task Distribute(BuildPath buildPath, TaskToken task) { task.Report(0, description: $"Pushing {buildPath.target}"); var path = OptionHelper.GetBuildBasePath(buildPath.path); var channel = ChannelNames[buildPath.target]; if (!string.IsNullOrEmpty(channelSuffix)) { channel += "-" + channelSuffix; } var version = Application.version; var buildInfo = BuildInfo.FromPath(path); if (buildInfo != null) { if (!buildInfo.version.IsDefined) { Debug.LogWarning("ItchDistro: build.json exists but contains no version."); } else { version = buildInfo.version.MajorMinorPatchBuild; } } var args = string.Format( "push '{0}' '{1}:{2}' --userversion '{3}' --ignore='*.DS_Store' --ignore='build.json'", path, project, channel, Application.version ); await Execute(new ExecutionArgs(butlerPath, args), task); }
protected IEnumerator Zip(BuildPath buildPath) { var target = buildPath.target; var path = buildPath.path; if (!File.Exists(path) && !Directory.Exists(path)) { Debug.LogError("ZipDistro: Path to compress does not exist: " + path); yield return(null); yield break; } if (ZipIgnorePatterns == null) { ZipIgnorePatterns = new Regex[ZipIgnore.Length]; for (int i = 0; i < ZipIgnore.Length; i++) { var regex = Regex.Escape(ZipIgnore[i]).Replace(@"\*", ".*").Replace(@"\?", "."); ZipIgnorePatterns[i] = new Regex(regex); } } var sevenZPath = Get7ZipPath(); if (sevenZPath == null) { yield return(null); yield break; } // Path can point to executable file but there might be files // in the containing directory we need as well var basePath = OptionHelper.GetBuildBasePath(path); // Check the files in containing directory var files = new List <string>(Directory.GetFileSystemEntries(basePath)); for (int i = files.Count - 1; i >= 0; i--) { var filename = Path.GetFileName(files[i]); foreach (var pattern in ZipIgnorePatterns) { if (pattern.IsMatch(filename)) { files.RemoveAt(i); goto ContinueOuter; } } ContinueOuter :; } if (files.Count == 0) { Debug.LogError("ZipDistro: Nothing to ZIP in directory: " + basePath); yield return(null); yield break; } // Determine output path first to make it consistent and use absolute path // since the script will be run in a different working directory var prettyName = GetPrettyName(target); if (prettyName == null) { prettyName = Path.GetFileNameWithoutExtension(basePath); } var versionSuffix = ""; if (appendVersion) { var buildInfo = BuildInfo.FromPath(path); if (buildInfo != null) { if (!buildInfo.version.IsDefined) { Debug.LogWarning("ZipDistro: build.json exists but contains no version"); } else { versionSuffix = " " + buildInfo.version.MajorMinorPatch; } } if (versionSuffix.Length == 0) { versionSuffix = " " + Application.version; } } var extension = FileExtensions[(int)format]; var zipName = prettyName + versionSuffix + extension; zipName = zipName.Replace(" ", "_"); var outputPath = Path.Combine(Path.GetDirectoryName(basePath), zipName); outputPath = Path.GetFullPath(outputPath); // Delete existing archive, otherwise 7za will update it if (File.Exists(outputPath)) { File.Delete(outputPath); } // In case it only contains a single file, just zip that file var singleFile = false; if (files.Count == 1) { singleFile = true; basePath = files[0]; } // Run 7za command to create ZIP file var excludes = ""; foreach (var pattern in ZipIgnore) { excludes += @" -xr\!'" + pattern + "'"; } var inputName = Path.GetFileName(basePath); var args = string.Format( "a '{0}' '{1}' -r -mx{2} {3}", outputPath, inputName, (int)compression, excludes ); var startInfo = new System.Diagnostics.ProcessStartInfo(); startInfo.FileName = sevenZPath; startInfo.Arguments = args; startInfo.WorkingDirectory = Path.GetDirectoryName(basePath); Debug.Log("ZipDistro: Archiving " + inputName); yield return(Execute(startInfo)); var exitcode = GetSubroutineResult <int>(); if (exitcode != 0) { yield return(null); yield break; } if (!singleFile && prettyName != inputName) { yield return(RenameRoot(outputPath, inputName, prettyName)); var success = GetSubroutineResult <bool>(); if (!success) { yield return(null); yield break; } } Debug.Log("ZipDistro: Archived to: " + outputPath); yield return(new BuildPath(target, outputPath)); }
/// <summary> /// Notarize a macOS build. /// </summary> /// <remarks> /// This method will throw if the given build is not a macOS build. /// </remarks> /// <param name="macBuildPath">Path to the app bundle</param> public async Task Notarize(BuildPath macBuildPath, TaskToken task) { if (macBuildPath.target != BuildTarget.StandaloneOSX) { throw new Exception($"NotarizationDistro: Notarization is only available for macOS builds (got {macBuildPath.target})"); } var path = macBuildPath.path; // Check settings if (string.IsNullOrEmpty(appSignIdentity)) { throw new Exception("NotarizationDistro: App sign identity not set."); } if (entitlements == null) { throw new Exception("NotarizationDistro: Entitlements file not set."); } // Check User if (string.IsNullOrEmpty(ascLogin.User)) { throw new Exception("NotarizationDistro: No App Store Connect user set."); } if (ascLogin.GetPassword(keychainService) == null) { throw new Exception("NotarizationDistro: No App Store Connect password found in Keychain."); } task.Report(0, 6, "Checking if app is already notarized"); // Try stapling in case the build has already been notarized if (await Staple(path, silentError: true, task)) { Debug.Log("Build already notarized, nothing more to do..."); return; } task.Report(1, description: "Signing app"); // Sign plugins // codesign --deep --force does not resign nested plugins, // --force only applies to the main bundle. If we want to // resign nested plugins, we have to call codesign for each. // This is required for library validation with the hardened runtime. var plugins = Path.Combine(path, "Contents/Plugins"); if (Directory.Exists(plugins)) { await Sign(Directory.GetFiles(plugins, "*.dylib", SearchOption.TopDirectoryOnly), task); await Sign(Directory.GetFiles(plugins, "*.bundle", SearchOption.TopDirectoryOnly), task); await Sign(Directory.GetDirectories(plugins, "*.bundle", SearchOption.TopDirectoryOnly), task); } // Sign application var entitlementsPath = AssetDatabase.GetAssetPath(entitlements); await Sign(path, task, entitlementsPath); task.Report(2, description: "Zipping app"); // Zip app var zipPath = path + ".zip"; await Zip(path, zipPath, task); task.Report(3, description: "Uploading app"); // Upload for notarization string requestUUID = null; try { requestUUID = await Upload(zipPath, task); if (requestUUID == null) { throw new Exception("NotarizationDistro: Could not parse request UUID from upload output"); } } finally { // Delete ZIP regardless of upload result File.Delete(zipPath); } task.Report(4, description: "Waiting for notarization result"); // Wait for notarization to complete var status = await WaitForCompletion(requestUUID, task); if (status != "success") { throw new Exception($"NotarizationDistro: Got '{status}' notarization status"); } task.Report(5, description: "Stapling ticket to app"); // Staple await Staple(path, silentError : false, task); }
/// <summary> /// Notarize a macOS build. /// </summary> /// <param name="path">Path to the app bundle</param> /// <returns>The string path to the notarized macOS app bundle</returns> public IEnumerator Notarize(BuildPath macBuildPath) { if (macBuildPath.target != BuildTarget.StandaloneOSX) { Debug.LogError($"NotarizationDistro: Notarization is only available for macOS builds (got {macBuildPath.target})"); yield return null; yield break; } var path = macBuildPath.path; // Check settings if (string.IsNullOrEmpty(appSignIdentity)) { Debug.LogError("NotarizationDistro: App sign identity not set."); yield return null; yield break; } if (entitlements == null) { Debug.LogError("NotarizationDistro: Entitlements file not set."); yield return null; yield break; } // Check User if (string.IsNullOrEmpty(ascLogin.User)) { Debug.LogError("NotarizationDistro: No App Store Connect user set."); yield return null; yield break; } if (ascLogin.GetPassword(keychainService) == null) { Debug.LogError("NotarizationDistro: No App Store Connect password found in Keychain."); yield return null; yield break; } Debug.Log("Checking if app is already notarized..."); // Try stapling in case the build has already been notarized yield return Staple(path, false); if (GetSubroutineResult<bool>()) { Debug.Log("Build already notarized, nothing more to do..."); yield return path; yield break; } Debug.Log("Singing app..."); // Sign plugins // codesign --deep --force does not resign nested plugins, // --force only applies to the main bundle. If we want to // resign nested plugins, we have to call codesign for each. // This is required for library validation with the hardened runtime. var plugins = Path.Combine(path, "Contents/Plugins"); if (Directory.Exists(plugins)) { yield return SignAll(Directory.GetFiles(plugins, "*.dylib", SearchOption.TopDirectoryOnly)); if (!GetSubroutineResult<bool>()) { yield return false; yield break; } yield return SignAll(Directory.GetFiles(plugins, "*.bundle", SearchOption.TopDirectoryOnly)); if (!GetSubroutineResult<bool>()) { yield return false; yield break; } yield return SignAll(Directory.GetDirectories(plugins, "*.bundle", SearchOption.TopDirectoryOnly)); if (!GetSubroutineResult<bool>()) { yield return false; yield break; } } // Sign application var entitlementsPath = AssetDatabase.GetAssetPath(entitlements); yield return Sign(path, entitlementsPath); if (!GetSubroutineResult<bool>()) { yield return null; yield break; } Debug.Log("Signed! Zipping app..."); // Zip app var zipPath = path + ".zip"; yield return Zip(path, zipPath); if (!GetSubroutineResult<bool>()) { yield return null; yield break; } Debug.Log("Zipped! Uploading..."); // Upload for notarization yield return Upload(zipPath); // Delete ZIP regardless of upload result File.Delete(zipPath); var requestUUID = GetSubroutineResult<string>(); if (requestUUID == null) { yield return null; yield break; } Debug.Log("Uploaded! Waiting for result..."); // Wait for notarization to complete yield return WaitForCompletion(requestUUID); var status = GetSubroutineResult<string>(); if (status != "success") { yield return null; yield break; } Debug.Log("Done! Stapling..."); // Staple yield return Staple(path, true); if (!GetSubroutineResult<bool>()) { yield return null; yield break; } Debug.Log("NotarizationDistro: Finished"); yield return path; }