Files
AR-Menu/Library/PackageCache/com.needle.engine-exporter@8c046140a1d9/Deploy/Editor/DeployToFTPEditor.cs
2025-11-30 08:35:03 +02:00

268 lines
8.8 KiB
C#

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Needle.Engine.Core;
using Needle.Engine.Utils;
using UnityEditor;
using UnityEngine;
namespace Needle.Engine.Deployment
{
[CustomEditor(typeof(DeployToFTP))]
public class DeployToFTPEditor : BaseDeployToEditor
{
private IProjectInfo project;
private SerializedProperty settings, path, useGzipCompression, overrideGzipCompression;
private void OnEnable()
{
project = ObjectUtils.FindObjectOfType<IProjectInfo>();
path = serializedObject.FindProperty(nameof(DeployToFTP.Path));
settings = serializedObject.FindProperty(nameof(DeployToFTP.FTPServer));
useGzipCompression = serializedObject.FindProperty(nameof(DeployToFTP.UseGzipCompression));
overrideGzipCompression = serializedObject.FindProperty(nameof(DeployToFTP.OverrideGzipCompression));
}
public override void OnInspectorGUI()
{
if (!OnBeforeInspectorGUI()) return;
if (project == null)
{
EditorGUILayout.HelpBox("No project found - please add a " + nameof(ExportInfo) + " component to you scene", MessageType.Warning);
return;
}
var ftp = target as DeployToFTP;
if (!ftp)
{
base.OnInspectorGUI();
return;
}
ftp.Path = ftp.Path?.TrimStart('.');
if (string.IsNullOrWhiteSpace(ftp.Path)) ftp.Path = "/";
var change = new EditorGUI.ChangeCheckScope();
EditorGUILayout.LabelField("FTP", EditorStyles.boldLabel);
using (new GUILayout.HorizontalScope())
{
EditorGUILayout.PropertyField(settings, new GUIContent("Server"));
if (!settings.objectReferenceValue)
{
if (GUILayout.Button("Create", GUILayout.Width(50)))
{
var instance = CreateInstance<FTPServer>();
AssetDatabase.CreateAsset(instance, "Assets/FTPServer.asset");
settings.objectReferenceValue = instance;
serializedObject.ApplyModifiedProperties();
}
}
}
var server = ftp.FTPServer;
string key = default;
if (server)
server.TryGetKey(out key);
var password = SecretsHelper.GetSecret(key);
var hasInvalidDeploymentPath = (server && !server.AllowTopLevelDeployment) &&
(string.IsNullOrWhiteSpace(path.stringValue) ||
path.stringValue.Trim() == "/");
if (server)
{
EditorGUILayout.PropertyField(path, new GUIContent("Path", "The path on the ftp server where you want to deploy your website to."));
if (hasInvalidDeploymentPath)
{
EditorGUILayout.HelpBox("Deployment to the top level directory is not allowed. Please specify a subfolder path.", MessageType.Warning);
}
using (new EditorGUILayout.HorizontalScope())
{
UseGizp.Enabled = EditorGUILayout.Toggle(new GUIContent("Use Gzip", "Enable gzip compression for the files. Make sure your server supports gzip compression."), UseGizp.Enabled);
}
}
if (change.changed)
serializedObject.ApplyModifiedProperties();
NeedleProjectConfig.TryGetBuildDirectory(out var directory);
if (!server)
{
EditorGUILayout.Space(2);
EditorGUILayout.HelpBox("Instructions:\n1) Create FTP Server settings\n2) Enter your server details there\n3) Assign the FTP Server settings here", MessageType.None);
}
EditorGUILayout.Space(5);
var hasPassword = !string.IsNullOrWhiteSpace(password);
var hasServerName = !string.IsNullOrWhiteSpace(server?.Servername);
var hasUsername = !string.IsNullOrWhiteSpace(server?.Username);
var canDeploy = server && hasPassword && hasServerName && hasUsername;
if (!canDeploy)
{
if (server)
{
var missing = hasServerName ? "" : "\n- server name";
if (!hasUsername) missing += "\n- username";
if (!hasPassword) missing += "\n- password";
EditorGUILayout.HelpBox($"FTP Server configuration is missing:{missing}", MessageType.None);
}
}
var isDeploying = currentTask != null && !currentTask.IsCompleted;
using (new EditorGUI.DisabledScope(!canDeploy || hasInvalidDeploymentPath))
{
using(new EditorGUI.DisabledScope(isDeploying))
using (new GUILayout.HorizontalScope())
{
var devBuild = NeedleEngineBuildOptions.DevelopmentBuild;
if ((Event.current.modifiers & EventModifiers.Alt) != 0) devBuild = !devBuild;
if (GUILayout.Button("Build & Deploy: " + (devBuild ? "Dev" : "Prod"), GUILayout.Height(30)))
{
RunDeployment(target as DeployToFTP, true, devBuild);
}
using (new EditorGUI.DisabledScope(!Directory.Exists(directory)))
{
if (GUILayout.Button("Deploy Only", GUILayout.Height(30)))
{
RunDeployment(target as DeployToFTP,false, devBuild);
}
}
}
}
var hasRemoteUrl = server && server.RemoteUrlIsValid;
if (hasRemoteUrl)
{
var fullUrl = server.GetUrl(ftp.Path);
// using (new EditorGUI.DisabledScope(!hasRemoteUrl))
if (GUILayout.Button(new GUIContent("Open in Browser " + Constants.ExternalLinkChar, fullUrl), GUILayout.Height(30)))
{
Application.OpenURL(fullUrl);
}
}
if (isDeploying)
{
EditorGUILayout.HelpBox("Deployment to FTP is in progress...", MessageType.None);
}
}
private static Task<bool> currentTask;
private static CancellationTokenSource cancel;
public static async void RunDeployment(DeployToFTP comp, bool exportScene, bool devBuild)
{
var projectInfo = ObjectUtils.FindObjectOfType<IProjectInfo>();
var server = comp.FTPServer;
var remotePath = comp.Path;
var username = server.Username;
server.TryGetKey(out var key);
var password = SecretsHelper.GetSecret(key);
var sftp = server.SFTP;
var port = server.Port;
Debug.Log("Begin uploading...");
cancel?.Cancel();
if (currentTask != null && currentTask.IsCompleted == false)
{
await currentTask;
return;
}
const int maxUploadDurationInMilliseconds = 10 * 60 * 1000;
cancel = new CancellationTokenSource(maxUploadDurationInMilliseconds);
var progId = Progress.Start("FTP Upload", "", Progress.Options.Managed);
Progress.RegisterCancelCallback(progId, () =>
{
if (!cancel.IsCancellationRequested)
{
Debug.Log("Cancelling FTP upload...");
cancel.Cancel();
}
return true;
});
BuildContext buildContext;
if (exportScene) buildContext = BuildContext.Distribution(!devBuild);
else buildContext = BuildContext.PrepareDeploy;
if (server.RemoteUrlIsValid)
{
var baseUrl = server.RemoteUrl;
// ensure we don't have double slashes (while keeping https:// intact)
while(baseUrl.EndsWith("/")) baseUrl = baseUrl.Substring(0, baseUrl.Length - 1);
while (remotePath.StartsWith("/")) remotePath = remotePath.Substring(1);
while(remotePath.Contains("//")) remotePath = remotePath.Replace("//", "/");
buildContext.LiveUrl = baseUrl + "/" + remotePath;
}
var distDirectory = projectInfo.ProjectDirectory + "/dist";
if (NeedleProjectConfig.TryGetBuildDirectory(out var dir))
{
distDirectory = dir;
}
var buildResult = false;
var postBuildMessage = default(string);
if (exportScene)
{
Progress.SetDescription(progId, "Export and Build");
var dev = NeedleEngineBuildOptions.DevelopmentBuild;
Debug.Log("<b>Begin building distribution</b>");
currentTask = Actions.ExportAndBuild(buildContext);
buildResult = await currentTask;
postBuildMessage = "<b>Successfully built distribution</b>";
}
else
{
currentTask = Actions.ExportAndBuild(buildContext);
buildResult = await currentTask;
}
if (cancel.IsCancellationRequested)
{
Debug.LogWarning("Upload cancelled");
return;
}
if (!buildResult)
{
Debug.LogError("Build failed, aborting FTP upload - see console for errors");
return;
}
if (postBuildMessage != null) Debug.Log(postBuildMessage);
Debug.Log("<b>Begin uploading</b> " + distDirectory);
Progress.SetDescription(progId, "Upload " + Path.GetDirectoryName(projectInfo.ProjectDirectory) + " to FTP");
if(SceneView.lastActiveSceneView) SceneView.lastActiveSceneView.ShowNotification(new GUIContent("Begin Upload..."), 5);
currentTask = Tools.UploadToFTP(server.Servername, username, password, distDirectory, remotePath, sftp, false, port, cancel.Token);
// currentTask = UploadDirectory(distDirectory, opts);
var uploadResult = await currentTask;
if (cancel.Token.IsCancellationRequested)
Debug.LogWarning("<b>FTP upload was cancelled</b>");
else if (uploadResult)
{
if(SceneView.lastActiveSceneView) SceneView.lastActiveSceneView.ShowNotification(new GUIContent("Upload succeeded"), 5);
Debug.Log($"<b>FTP upload {"succeeded".AsSuccess()}</b> " + distDirectory);
if (!string.IsNullOrWhiteSpace(buildContext.LiveUrl))
{
Application.OpenURL(buildContext.LiveUrl);
}
}
else Debug.LogError("Uploading failed. Please see console for errors.\n" + distDirectory);
if (Progress.Exists(progId))
Progress.Finish(progId);
}
}
}