diff --git a/.gitignore b/.gitignore index 67d4e53bc..7fc38bf13 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,5 @@ _NCrunch_GitHub.Unity .DS_Store build/ TestResult.xml -submodules/ *.stackdump *.lastcodeanalysissucceeded \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index d9e14a3e9..8ecda521f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "script"] path = script url = git@github.com:github-for-unity/UnityBuildScripts +[submodule "submodules/packaging"] + path = submodules/packaging + url = https://github.com/github-for-unity/packaging diff --git a/create-octorun-zip.sh b/create-octorun-zip.sh new file mode 100755 index 000000000..e722d7cb2 --- /dev/null +++ b/create-octorun-zip.sh @@ -0,0 +1,3 @@ +#!/bin/sh -eu +DIR=$(pwd) +submodules/packaging/octorun/run.sh --path $DIR/octorun --out $DIR/src/GitHub.Api/Resources diff --git a/octorun/src/api.js b/octorun/src/api.js index 1c8c37e42..5b1bc833e 100644 --- a/octorun/src/api.js +++ b/octorun/src/api.js @@ -6,8 +6,8 @@ function ApiWrapper() { throw "appName missing"; } - if (!config.user || !config.token) { - throw "user and/or token missing"; + if (!config.token) { + throw "token missing"; } this.octokit = octokitWrapper.createOctokit(config.appName); diff --git a/octorun/src/authentication.js b/octorun/src/authentication.js index adfe688cd..e2d70d952 100644 --- a/octorun/src/authentication.js +++ b/octorun/src/authentication.js @@ -1,4 +1,3 @@ -var endOfLine = require('os').EOL; var config = require("./configuration"); var octokitWrapper = require("./octokit"); diff --git a/octorun/src/bin/app-login.js b/octorun/src/bin/app-login.js index f3484c31b..6c2ab582a 100644 --- a/octorun/src/bin/app-login.js +++ b/octorun/src/bin/app-login.js @@ -1,7 +1,6 @@ var commander = require("commander"); var package = require('../../package.json'); var authentication = require('../authentication'); -var endOfLine = require('os').EOL; var output = require('../output'); commander diff --git a/octorun/src/bin/app-organizations.js b/octorun/src/bin/app-organizations.js index 480289aa1..c9d70a760 100644 --- a/octorun/src/bin/app-organizations.js +++ b/octorun/src/bin/app-organizations.js @@ -1,7 +1,6 @@ var commander = require("commander"); var package = require('../../package.json'); var ApiWrapper = require('../api'); -var endOfLine = require('os').EOL; var output = require('../output'); commander diff --git a/octorun/src/bin/app-publish.js b/octorun/src/bin/app-publish.js index 5fe602390..61d132326 100644 --- a/octorun/src/bin/app-publish.js +++ b/octorun/src/bin/app-publish.js @@ -1,7 +1,6 @@ var commander = require("commander"); var package = require('../../package.json') var ApiWrapper = require('../api') -var endOfLine = require('os').EOL; var output = require('../output'); commander diff --git a/octorun/src/bin/app-usage.js b/octorun/src/bin/app-usage.js index 9f6811d15..e8caf544f 100644 --- a/octorun/src/bin/app-usage.js +++ b/octorun/src/bin/app-usage.js @@ -1,9 +1,7 @@ -var commander = require("commander"); -var package = require('../../package.json') -var config = require("../configuration"); -var endOfLine = require('os').EOL; +var commander = require('commander'); +var package = require('../../package.json'); +var config = require('../configuration'); var fs = require('fs'); -var util = require('util'); var output = require('../output'); commander diff --git a/octorun/src/bin/app-validate.js b/octorun/src/bin/app-validate.js index 8ba643021..7fd53bbb9 100644 --- a/octorun/src/bin/app-validate.js +++ b/octorun/src/bin/app-validate.js @@ -1,6 +1,5 @@ var commander = require("commander"); var package = require('../../package.json'); -var endOfLine = require('os').EOL; var ApiWrapper = require('../api'); var output = require('../output'); diff --git a/octorun/src/configuration.js b/octorun/src/configuration.js index f9462acde..0fe3906fd 100644 --- a/octorun/src/configuration.js +++ b/octorun/src/configuration.js @@ -3,13 +3,11 @@ require("dotenv").config({silent: true}); var clientId = process.env.OCTOKIT_CLIENT_ID; var clientSecret = process.env.OCTOKIT_CLIENT_SECRET; var appName = process.env.OCTOKIT_USER_AGENT; -var user = process.env.OCTORUN_USER; var token = process.env.OCTORUN_TOKEN; module.exports = { clientId: clientId, clientSecret: clientSecret, appName: appName, - user: user, token: token }; \ No newline at end of file diff --git a/octorun/version b/octorun/version index 48417770b..820d50296 100644 --- a/octorun/version +++ b/octorun/version @@ -1 +1 @@ -7f160da1 \ No newline at end of file +f497f7aa3d \ No newline at end of file diff --git a/script b/script index 259dba7e8..0c618caaa 160000 --- a/script +++ b/script @@ -1 +1 @@ -Subproject commit 259dba7e8375a96a935b51e2169fe029cd70c039 +Subproject commit 0c618caaab7f163921aea7b60484b423e13cb818 diff --git a/src/GitHub.Api/Application/ApiClient.cs b/src/GitHub.Api/Application/ApiClient.cs index 73055228b..065c86c85 100644 --- a/src/GitHub.Api/Application/ApiClient.cs +++ b/src/GitHub.Api/Application/ApiClient.cs @@ -16,23 +16,25 @@ class ApiClient : IApiClient private readonly IKeychain keychain; private readonly IProcessManager processManager; private readonly ITaskManager taskManager; - private readonly NPath nodeJsExecutablePath; - private readonly NPath octorunScriptPath; private readonly ILoginManager loginManager; + private readonly IEnvironment environment; - public ApiClient(UriString hostUrl, IKeychain keychain, IProcessManager processManager, ITaskManager taskManager, NPath nodeJsExecutablePath, NPath octorunScriptPath) + public ApiClient(UriString hostUrl, IKeychain keychain, IProcessManager processManager, ITaskManager taskManager, IEnvironment environment) { - Guard.ArgumentNotNull(hostUrl, nameof(hostUrl)); Guard.ArgumentNotNull(keychain, nameof(keychain)); - HostAddress = HostAddress.Create(hostUrl); - OriginalUrl = hostUrl; + var host = String.IsNullOrEmpty(hostUrl) + ? UriString.ToUriString(HostAddress.GitHubDotComHostAddress.WebUri) + : new UriString(hostUrl.ToRepositoryUri() + .GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped)); + + HostAddress = HostAddress.Create(host); + OriginalUrl = host; this.keychain = keychain; this.processManager = processManager; this.taskManager = taskManager; - this.nodeJsExecutablePath = nodeJsExecutablePath; - this.octorunScriptPath = octorunScriptPath; - loginManager = new LoginManager(keychain, processManager, taskManager, nodeJsExecutablePath, octorunScriptPath); + this.environment = environment; + loginManager = new LoginManager(keychain, processManager, taskManager, environment); } public ITask Logout(UriString host) @@ -40,14 +42,15 @@ public ITask Logout(UriString host) return loginManager.Logout(host); } - public void CreateRepository(string name, string description, bool isPrivate, Action callback, string organization = null) + public void CreateRepository(string name, string description, bool isPrivate, + Action callback, string organization = null) { Guard.ArgumentNotNull(callback, "callback"); new FuncTask(taskManager.Token, () => { - var user = GetCurrentUser(); - var keychainAdapter = keychain.Connect(OriginalUrl); + // this validates the user, again + GetCurrentUser(); var command = new StringBuilder("publish -r \""); command.Append(name); @@ -72,8 +75,7 @@ public void CreateRepository(string name, string description, bool isPrivate, Ac command.Append(" -p"); } - var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath, octorunScriptPath, command.ToString(), - user: user.Login, userToken: keychainAdapter.Credential.Token) + var octorunTask = new OctorunTask(taskManager.Token, keychain, environment, command.ToString()) .Configure(processManager); var ret = octorunTask.RunSynchronously(); @@ -106,11 +108,8 @@ public void GetOrganizations(Action onSuccess, Action Guard.ArgumentNotNull(onSuccess, nameof(onSuccess)); new FuncTask(taskManager.Token, () => { - var user = GetCurrentUser(); - var keychainAdapter = keychain.Connect(OriginalUrl); - - var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath, octorunScriptPath, "organizations", - user: user.Login, userToken: keychainAdapter.Credential.Token) + var octorunTask = new OctorunTask(taskManager.Token, keychain, environment, + "organizations") .Configure(processManager); var ret = octorunTask.RunSynchronously(); @@ -208,8 +207,7 @@ public void ContinueLogin(LoginResult loginResult, string code) private GitHubUser GetCurrentUser() { - //TODO: ONE_USER_LOGIN This assumes we only support one login - var keychainConnection = keychain.Connections.FirstOrDefault(); + var keychainConnection = keychain.Connections.FirstOrDefault(x => x.Host == OriginalUrl); if (keychainConnection == null) throw new KeychainEmptyException(); @@ -227,7 +225,7 @@ private GitHubUser GetCurrentUser() private IKeychainAdapter GetValidatedKeychainAdapter(Connection keychainConnection) { - var keychainAdapter = keychain.Load(keychainConnection.Host); + var keychainAdapter = keychain.LoadFromSystem(keychainConnection.Host); if (keychainAdapter == null) throw new KeychainEmptyException(); @@ -249,8 +247,7 @@ private GitHubUser GetValidatedGitHubUser(Connection keychainConnection, IKeycha { try { - var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath, octorunScriptPath, "validate", - user: keychainConnection.Username, userToken: keychainAdapter.Credential.Token) + var octorunTask = new OctorunTask(taskManager.Token, keychain, environment, "validate") .Configure(processManager); var ret = octorunTask.RunSynchronously(); diff --git a/src/GitHub.Api/Application/ApplicationManagerBase.cs b/src/GitHub.Api/Application/ApplicationManagerBase.cs index 53370df38..76f8d59a1 100644 --- a/src/GitHub.Api/Application/ApplicationManagerBase.cs +++ b/src/GitHub.Api/Application/ApplicationManagerBase.cs @@ -53,9 +53,8 @@ protected void Initialize() #if ENABLE_METRICS var metricsService = new MetricsService(ProcessManager, TaskManager, - Environment.FileSystem, - Environment.NodeJsExecutablePath, - Environment.OctorunScriptPath); + Platform.Keychain, + Environment); UsageTracker.MetricsService = metricsService; #endif } diff --git a/src/GitHub.Api/Authentication/Credential.cs b/src/GitHub.Api/Authentication/Credential.cs index 2e31f9838..86e76c458 100644 --- a/src/GitHub.Api/Authentication/Credential.cs +++ b/src/GitHub.Api/Authentication/Credential.cs @@ -16,7 +16,7 @@ public Credential(UriString host, string username, string token) this.Token = token; } - public void UpdateToken(string token, string username) + public void Update(string token, string username) { this.Token = token; this.Username = username; diff --git a/src/GitHub.Api/Authentication/ICredentialManager.cs b/src/GitHub.Api/Authentication/ICredentialManager.cs index 68bf53eb6..dde06bcc7 100644 --- a/src/GitHub.Api/Authentication/ICredentialManager.cs +++ b/src/GitHub.Api/Authentication/ICredentialManager.cs @@ -8,7 +8,7 @@ public interface ICredential : IDisposable UriString Host { get; } string Username { get; } string Token { get; } - void UpdateToken(string token, string username); + void Update(string token, string username); } public interface ICredentialManager diff --git a/src/GitHub.Api/Authentication/IKeychain.cs b/src/GitHub.Api/Authentication/IKeychain.cs index 92d5dc524..4a14e1e2f 100644 --- a/src/GitHub.Api/Authentication/IKeychain.cs +++ b/src/GitHub.Api/Authentication/IKeychain.cs @@ -6,15 +6,13 @@ namespace GitHub.Unity public interface IKeychain { IKeychainAdapter Connect(UriString host); - IKeychainAdapter Load(UriString host); + IKeychainAdapter LoadFromSystem(UriString host); void Clear(UriString host, bool deleteFromCredentialManager); - void Save(UriString host); - void SetCredentials(ICredential credential); + void SaveToSystem(UriString host); void Initialize(); Connection[] Connections { get; } IList Hosts { get; } bool HasKeys { get; } - void SetToken(UriString host, string token, string username); event Action ConnectionsChanged; } diff --git a/src/GitHub.Api/Authentication/Keychain.cs b/src/GitHub.Api/Authentication/Keychain.cs index 992cc26b3..a32cc3c42 100644 --- a/src/GitHub.Api/Authentication/Keychain.cs +++ b/src/GitHub.Api/Authentication/Keychain.cs @@ -95,19 +95,16 @@ public Keychain(IEnvironment environment, ICredentialManager credentialManager) public IKeychainAdapter Connect(UriString host) { Guard.ArgumentNotNull(host, nameof(host)); - return FindOrCreateAdapter(host); } - public IKeychainAdapter Load(UriString host) + public IKeychainAdapter LoadFromSystem(UriString host) { Guard.ArgumentNotNull(host, nameof(host)); - var keychainAdapter = FindOrCreateAdapter(host); - var connection = GetConnection(host); - - var keychainItem = credentialManager.Load(host); - if (keychainItem == null) + var keychainAdapter = Connect(host) as KeychainAdapter; + var credential = credentialManager.Load(host); + if (credential == null) { logger.Warning("Cannot load host from Credential Manager; removing from cache"); Clear(host, false); @@ -115,12 +112,18 @@ public IKeychainAdapter Load(UriString host) } else { - if (keychainItem.Username != connection.Username) + keychainAdapter.Set(credential); + var connection = GetConnection(host); + if (connection.Username == null) { - logger.Warning("Keychain Username:\"{0}\" does not match cached Username:\"{1}\"; Hopefully it works", keychainItem.Username, connection.Username); + connection.Username = credential.Username; + SaveConnectionsToDisk(); } - keychainAdapter.Set(keychainItem); + if (credential.Username != connection.Username) + { + logger.Warning("Keychain Username:\"{0}\" does not match cached Username:\"{1}\"; Hopefully it works", credential.Username, connection.Username); + } } return keychainAdapter; } @@ -151,7 +154,7 @@ public void Clear(UriString host, bool deleteFromCredentialManager) RemoveCredential(host, deleteFromCredentialManager); } - public void Save(UriString host) + public void SaveToSystem(UriString host) { Guard.ArgumentNotNull(host, nameof(host)); @@ -159,24 +162,6 @@ public void Save(UriString host) AddConnection(new Connection(host, keychainAdapter.Credential.Username)); } - public void SetCredentials(ICredential credential) - { - Guard.ArgumentNotNull(credential, nameof(credential)); - - var keychainAdapter = GetKeychainAdapter(credential.Host); - keychainAdapter.Set(credential); - } - - public void SetToken(UriString host, string token, string username) - { - Guard.ArgumentNotNull(host, nameof(host)); - Guard.ArgumentNotNull(token, nameof(token)); - Guard.ArgumentNotNull(username, nameof(username)); - - var keychainAdapter = GetKeychainAdapter(host); - keychainAdapter.UpdateToken(token, username); - } - private void LoadConnectionsFromDisk() { if (cachePath.FileExists()) @@ -262,11 +247,11 @@ private void RemoveCredential(UriString host, bool deleteFromCredentialManager) private Connection GetConnection(UriString host) { if (!connections.ContainsKey(host)) - throw new ArgumentException($"{host} is not found", nameof(host)); + return AddConnection(new Connection(host, null)); return connections[host]; } - private void AddConnection(Connection connection) + private Connection AddConnection(Connection connection) { // create new connection in the connection cache for this host if (connections.ContainsKey(connection.Host)) @@ -274,6 +259,7 @@ private void AddConnection(Connection connection) else connections.Add(connection.Host, connection); SaveConnectionsToDisk(); + return connection; } private void RemoveConnection(UriString host) diff --git a/src/GitHub.Api/Authentication/KeychainAdapter.cs b/src/GitHub.Api/Authentication/KeychainAdapter.cs index abbe9895e..9f5d30d5c 100644 --- a/src/GitHub.Api/Authentication/KeychainAdapter.cs +++ b/src/GitHub.Api/Authentication/KeychainAdapter.cs @@ -9,9 +9,9 @@ public void Set(ICredential credential) Credential = credential; } - public void UpdateToken(string token, string username) + public void Update(string token, string username) { - Credential.UpdateToken(token, username); + Credential.Update(token, username); } public void Clear() @@ -23,5 +23,8 @@ public void Clear() public interface IKeychainAdapter { ICredential Credential { get; } + void Set(ICredential credential); + void Update(string token, string username); + void Clear(); } } diff --git a/src/GitHub.Api/Authentication/LoginManager.cs b/src/GitHub.Api/Authentication/LoginManager.cs index 44a337a54..9a67986af 100644 --- a/src/GitHub.Api/Authentication/LoginManager.cs +++ b/src/GitHub.Api/Authentication/LoginManager.cs @@ -22,8 +22,7 @@ class LoginManager : ILoginManager private readonly IKeychain keychain; private readonly IProcessManager processManager; private readonly ITaskManager taskManager; - private readonly NPath? nodeJsExecutablePath; - private readonly NPath? octorunScript; + private readonly IEnvironment environment; /// /// Initializes a new instance of the class. @@ -35,15 +34,14 @@ class LoginManager : ILoginManager /// public LoginManager( IKeychain keychain, IProcessManager processManager, ITaskManager taskManager, - NPath? nodeJsExecutablePath = null, NPath? octorunScript = null) + IEnvironment environment) { Guard.ArgumentNotNull(keychain, nameof(keychain)); this.keychain = keychain; this.processManager = processManager; this.taskManager = taskManager; - this.nodeJsExecutablePath = nodeJsExecutablePath; - this.octorunScript = octorunScript; + this.environment = environment; } /// @@ -58,8 +56,8 @@ public LoginResultData Login( // Start by saving the username and password, these will be used by the `IGitHubClient` // until an authorization token has been created and acquired: - keychain.Connect(host); - keychain.SetCredentials(new Credential(host, username, password)); + var keychainAdapter = keychain.Connect(host); + keychainAdapter.Set(new Credential(host, username, password)); try { @@ -71,16 +69,13 @@ public LoginResultData Login( throw new InvalidOperationException("Returned token is null or empty"); } - if (loginResultData.Code == LoginResultCodes.Success) - { - username = RetrieveUsername(loginResultData, username); - } - - keychain.SetToken(host, loginResultData.Token, username); + keychainAdapter.Update(loginResultData.Token, username); if (loginResultData.Code == LoginResultCodes.Success) { - keychain.Save(host); + username = RetrieveUsername(loginResultData, username); + keychainAdapter.Update(loginResultData.Token, username); + keychain.SaveToSystem(host); } return loginResultData; @@ -101,6 +96,9 @@ public LoginResultData ContinueLogin(LoginResultData loginResultData, string two { var host = loginResultData.Host; var keychainAdapter = keychain.Connect(host); + if (keychainAdapter.Credential == null) { + return new LoginResultData(LoginResultCodes.Failed, Localization.LoginFailed, host); + } var username = keychainAdapter.Credential.Username; var password = keychainAdapter.Credential.Token; try @@ -114,9 +112,10 @@ public LoginResultData ContinueLogin(LoginResultData loginResultData, string two throw new InvalidOperationException("Returned token is null or empty"); } + keychainAdapter.Update(loginResultData.Token, username); username = RetrieveUsername(loginResultData, username); - keychain.SetToken(host, loginResultData.Token, username); - keychain.Save(host); + keychainAdapter.Update(loginResultData.Token, username); + keychain.SaveToSystem(host); return loginResultData; } @@ -146,22 +145,12 @@ private LoginResultData TryLogin( string code = null ) { - if (!nodeJsExecutablePath.HasValue) - { - throw new InvalidOperationException("nodeJsExecutablePath must be set"); - } - - if (!octorunScript.HasValue) - { - throw new InvalidOperationException("octorunScript must be set"); - } - var hasTwoFactorCode = code != null; var arguments = hasTwoFactorCode ? "login --twoFactor" : "login"; - var loginTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath.Value, octorunScript.Value, - arguments, ApplicationInfo.ClientId, ApplicationInfo.ClientSecret); - loginTask.Configure(processManager, workingDirectory: octorunScript.Value.Parent.Parent, withInput: true); + var loginTask = new OctorunTask(taskManager.Token, keychain, environment, + arguments); + loginTask.Configure(processManager, withInput: true); loginTask.OnStartProcess += proc => { proc.StandardInput.WriteLine(username); @@ -198,8 +187,8 @@ private string RetrieveUsername(LoginResultData loginResultData, string username return username; } - var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath.Value, octorunScript.Value, "validate", - user: username, userToken: loginResultData.Token).Configure(processManager); + var octorunTask = new OctorunTask(taskManager.Token, keychain, environment, "validate") + .Configure(processManager); var validateResult = octorunTask.RunSynchronously(); if (!validateResult.IsSuccess) diff --git a/src/GitHub.Api/Git/GitCredentialManager.cs b/src/GitHub.Api/Git/GitCredentialManager.cs index 25b946d1b..2dcb61eb7 100644 --- a/src/GitHub.Api/Git/GitCredentialManager.cs +++ b/src/GitHub.Api/Git/GitCredentialManager.cs @@ -60,7 +60,7 @@ public ICredential Load(UriString host) if (String.IsNullOrEmpty(kvpCreds)) { - Logger.Error("No credentials are stored"); + // we didn't find credentials, stop here return null; } diff --git a/src/GitHub.Api/Installer/OctorunInstaller.cs b/src/GitHub.Api/Installer/OctorunInstaller.cs index 1a319c6c4..9c5364eb1 100644 --- a/src/GitHub.Api/Installer/OctorunInstaller.cs +++ b/src/GitHub.Api/Installer/OctorunInstaller.cs @@ -85,7 +85,7 @@ public class OctorunInstallDetails public const string DefaultZipMd5Url = "http://github-vs.s3.amazonaws.com/unity/octorun/octorun.zip.md5"; public const string DefaultZipUrl = "http://github-vs.s3.amazonaws.com/unity/octorun/octorun.zip"; - public const string PackageVersion = "7f160da1"; + public const string PackageVersion = "f497f7aa3d"; private const string PackageName = "octorun"; private const string zipFile = "octorun.zip"; diff --git a/src/GitHub.Api/Primitives/UriString.cs b/src/GitHub.Api/Primitives/UriString.cs index a60decfb7..17c725ca3 100644 --- a/src/GitHub.Api/Primitives/UriString.cs +++ b/src/GitHub.Api/Primitives/UriString.cs @@ -28,7 +28,8 @@ public class UriString : StringEquivalent, IEquatable public UriString(string uriString) : base(NormalizePath(uriString)) { if (uriString == null || uriString.Length == 0) return; - if (Uri.TryCreate(uriString, UriKind.Absolute, out url)) + if (Uri.TryCreate(uriString, UriKind.Absolute, out url) + || Uri.TryCreate("https://" + uriString, UriKind.Absolute, out url)) { if (!url.IsFile) SetUri(url); diff --git a/src/GitHub.Api/Resources/octorun.zip b/src/GitHub.Api/Resources/octorun.zip index d7c63b3f3..791c35d52 100644 --- a/src/GitHub.Api/Resources/octorun.zip +++ b/src/GitHub.Api/Resources/octorun.zip @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60478c33970a7a8d952777729450a42fa02bf7832514b7d44ec116aa09cb7814 -size 219592 +oid sha256:c7287864b7d86835175494c3743832f47a10481ab7fb1c257f871b808744e448 +size 1399629 diff --git a/src/GitHub.Api/Resources/octorun.zip.md5 b/src/GitHub.Api/Resources/octorun.zip.md5 index 6f12f7867..7e86214a0 100644 --- a/src/GitHub.Api/Resources/octorun.zip.md5 +++ b/src/GitHub.Api/Resources/octorun.zip.md5 @@ -1 +1 @@ -3d4bea9ae4ca3c4497d6b349166a1e34 \ No newline at end of file +a41ad2fd5ceaacb20574a0fc2841e82d \ No newline at end of file diff --git a/src/GitHub.Api/Tasks/OctorunTask.cs b/src/GitHub.Api/Tasks/OctorunTask.cs index f64fcc0cc..b094bd6e7 100644 --- a/src/GitHub.Api/Tasks/OctorunTask.cs +++ b/src/GitHub.Api/Tasks/OctorunTask.cs @@ -54,28 +54,41 @@ class OctorunTask : ProcessTask { private readonly string clientId; private readonly string clientSecret; - private readonly string user; private readonly string userToken; private readonly NPath pathToNodeJs; private readonly NPath pathToOctorunJs; private readonly string arguments; - public OctorunTask(CancellationToken token, NPath pathToNodeJs, NPath pathToOctorunJs, string arguments, - string clientId = null, - string clientSecret = null, - string user = null, - string userToken = null, + public OctorunTask(CancellationToken token, IKeychain keychain, IEnvironment environment, + string arguments, IOutputProcessor processor = null) : base(token, processor ?? new OctorunResultOutputProcessor()) { - this.clientId = clientId; - this.clientSecret = clientSecret; - this.user = user; - this.userToken = userToken; - this.pathToNodeJs = pathToNodeJs; - this.pathToOctorunJs = pathToOctorunJs; + this.clientId = ApplicationInfo.ClientId; + this.clientSecret = ApplicationInfo.ClientSecret; + this.pathToNodeJs = environment.NodeJsExecutablePath; + this.pathToOctorunJs = environment.OctorunScriptPath; this.arguments = $"\"{pathToOctorunJs}\" {arguments}"; + + var cloneUrl = environment.Repository?.CloneUrl; + var host = String.IsNullOrEmpty(cloneUrl) + ? UriString.ToUriString(HostAddress.GitHubDotComHostAddress.WebUri) + : new UriString(cloneUrl.ToRepositoryUri() + .GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped)); + + var adapter = keychain.Connect(host); + if (adapter.Credential?.Token != null) + { + userToken = adapter.Credential.Token; + } + else + { + // use a cached adapter if there is one filled out + adapter = keychain.LoadFromSystem(host); + if (adapter != null) + userToken = adapter.Credential.Token; + } } public override void Configure(ProcessStartInfo psi) @@ -85,21 +98,8 @@ public override void Configure(ProcessStartInfo psi) psi.WorkingDirectory = pathToOctorunJs.Parent.Parent.Parent; psi.EnvironmentVariables.Add("OCTOKIT_USER_AGENT", $"{ApplicationInfo.ApplicationSafeName}/{ApplicationInfo.Version}"); - - if (clientId != null) - { - psi.EnvironmentVariables.Add("OCTOKIT_CLIENT_ID", clientId); - } - - if (clientSecret != null) - { - psi.EnvironmentVariables.Add("OCTOKIT_CLIENT_SECRET", clientSecret); - } - - if (user != null) - { - psi.EnvironmentVariables.Add("OCTORUN_USER", user); - } + psi.EnvironmentVariables.Add("OCTOKIT_CLIENT_ID", clientId); + psi.EnvironmentVariables.Add("OCTOKIT_CLIENT_SECRET", clientSecret); if (userToken != null) { diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/Services/AuthenticationService.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/Services/AuthenticationService.cs index bd215045e..5c6afae20 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/Services/AuthenticationService.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/Services/AuthenticationService.cs @@ -8,9 +8,12 @@ class AuthenticationService private LoginResult loginResultData; - public AuthenticationService(IProcessManager processManager, ITaskManager taskManager, UriString host, IKeychain keychain, NPath nodeJsExecutablePath, NPath octorunExecutablePath) + public AuthenticationService(UriString host, IKeychain keychain, + IProcessManager processManager, ITaskManager taskManager, + IEnvironment environment + ) { - client = new ApiClient(host, keychain, processManager, taskManager, nodeJsExecutablePath, octorunExecutablePath); + client = new ApiClient(host, keychain, processManager, taskManager, environment); } public void Login(string username, string password, Action twofaRequired, Action authResult) diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/AuthenticationView.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/AuthenticationView.cs index b9430852c..6082238cb 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/AuthenticationView.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/AuthenticationView.cs @@ -243,18 +243,8 @@ private AuthenticationService AuthenticationService { if (authenticationService == null) { - UriString host; - if (Repository != null && Repository.CloneUrl != null && Repository.CloneUrl.IsValidUri) - { - host = new UriString(Repository.CloneUrl.ToRepositoryUri() - .GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped)); - } - else - { - host = UriString.ToUriString(HostAddress.GitHubDotComHostAddress.WebUri); - } - - AuthenticationService = new AuthenticationService(Manager.ProcessManager, Manager.TaskManager, host, Platform.Keychain, Environment.NodeJsExecutablePath, Environment.OctorunScriptPath); + UriString host = Repository != null ? Repository.CloneUrl : null; + AuthenticationService = new AuthenticationService(host, Platform.Keychain, Manager.ProcessManager, Manager.TaskManager, Environment); } return authenticationService; } diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PopupWindow.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PopupWindow.cs index ea7cd5e6d..d7da7cfbe 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PopupWindow.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PopupWindow.cs @@ -175,17 +175,8 @@ public IApiClient Client if (client == null) { var repository = Environment.Repository; - UriString host; - if (repository != null && !string.IsNullOrEmpty(repository.CloneUrl)) - { - host = repository.CloneUrl.ToRepositoryUrl(); - } - else - { - host = UriString.ToUriString(HostAddress.GitHubDotComHostAddress.WebUri); - } - - client = new ApiClient(host, Platform.Keychain, Manager.ProcessManager, TaskManager, Environment.NodeJsExecutablePath, Environment.OctorunScriptPath); + UriString host = repository != null ? repository.CloneUrl : null; + client = new ApiClient(host, Platform.Keychain, Manager.ProcessManager, TaskManager, Environment); } return client; diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PublishView.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PublishView.cs index 65ef5fb71..0671bcf06 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PublishView.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PublishView.cs @@ -35,24 +35,15 @@ class PublishView : Subview [NonSerialized] private string error; [NonSerialized] private bool ownersNeedLoading; + private Connection Connection { get { return Platform.Keychain.Connections.First(); } } + public IApiClient Client { get { if (client == null) { - var repository = Environment.Repository; - UriString host; - if (repository != null && !string.IsNullOrEmpty(repository.CloneUrl)) - { - host = repository.CloneUrl.ToRepositoryUrl(); - } - else - { - host = UriString.ToUriString(HostAddress.GitHubDotComHostAddress.WebUri); - } - - client = new ApiClient(host, Platform.Keychain, Manager.ProcessManager, TaskManager, Environment.NodeJsExecutablePath, Environment.OctorunScriptPath); + client = new ApiClient(Connection.Host, Platform.Keychain, Manager.ProcessManager, TaskManager, Environment); } return client; @@ -89,13 +80,10 @@ public override void InitializeView(IView parent) private void LoadOwners() { - var keychainConnections = Platform.Keychain.Connections; - //TODO: ONE_USER_LOGIN This assumes only ever one user can login - isBusy = true; //TODO: ONE_USER_LOGIN This assumes only ever one user can login - username = keychainConnections.First().Username; + username = Connection.Username; Client.GetOrganizations(orgs => { @@ -129,7 +117,7 @@ public override void OnGUI() { GUILayout.BeginHorizontal(Styles.AuthHeaderBoxStyle); { - GUILayout.Label(PublishToGithubLabel, EditorStyles.boldLabel); + GUILayout.Label(PublishToGithubLabel, EditorStyles.boldLabel); } GUILayout.EndHorizontal(); @@ -202,7 +190,7 @@ private string GetPublishErrorMessage(Exception ex) { return PublishLimitPrivateRepositoriesError; } - + return ex.Message; } diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/Window.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/Window.cs index 896160fe9..90557f47f 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/Window.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/Window.cs @@ -213,7 +213,22 @@ private void ValidateCachedData(IRepository repository) private void MaybeUpdateData() { - connection = Platform.Keychain.Connections.FirstOrDefault(); + UriString host = null; + if (!HasRepository || String.IsNullOrEmpty(Repository.CloneUrl)) + { + var firstConnection = Platform.Keychain.Connections.FirstOrDefault(); + if (firstConnection != null) + host = firstConnection.Host; + else + host = UriString.ToUriString(HostAddress.GitHubDotComHostAddress.WebUri); + } + else + { + host = new UriString(Repository.CloneUrl.ToRepositoryUri() + .GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped)); + } + + connection = Platform.Keychain.Connections.FirstOrDefault(x => x.Host.ToUriString() == host); if (repositoryProgressHasUpdate) { @@ -568,6 +583,24 @@ private void DoToolbarGUI() } GUILayout.FlexibleSpace(); + + if (!HasRepository) + { + GUILayout.FlexibleSpace(); + + if (connection == null) + { + if (GUILayout.Button("Sign in", EditorStyles.toolbarButton)) + SignIn(null); + } + else + { + if (GUILayout.Button(connection.Username, EditorStyles.toolbarDropDown)) + { + DoAccountDropdown(); + } + } + } } EditorGUILayout.EndHorizontal(); } @@ -826,27 +859,14 @@ private void SignIn(object obj) private void GoToProfile(object obj) { - //TODO: ONE_USER_LOGIN This assumes only ever one user can login - var keychainConnection = Platform.Keychain.Connections.First(); - var uriString = new UriString(keychainConnection.Host).Combine(keychainConnection.Username); + var uriString = new UriString(connection.Host).Combine(connection.Username); Application.OpenURL(uriString); } private void SignOut(object obj) { - UriString host; - if (Repository != null && Repository.CloneUrl != null && Repository.CloneUrl.IsValidUri) - { - host = new UriString(Repository.CloneUrl.ToRepositoryUri() - .GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped)); - } - else - { - host = UriString.ToUriString(HostAddress.GitHubDotComHostAddress.WebUri); - } - - var apiClient = new ApiClient(host, Platform.Keychain, Manager.ProcessManager, Manager.TaskManager, Environment.NodeJsExecutablePath, Environment.OctorunScriptPath); - apiClient.Logout(host).FinallyInUI((s, e) => Redraw()); + var loginManager = new LoginManager(Platform.Keychain, Manager.ProcessManager, Manager.TaskManager, Environment); + loginManager.Logout(connection.Host).FinallyInUI((s, e) => Redraw()); } public new void ShowNotification(GUIContent content) diff --git a/src/tests/IntegrationTests/BaseIntegrationTest.cs b/src/tests/IntegrationTests/BaseIntegrationTest.cs index 3041951e2..37b9a6c41 100644 --- a/src/tests/IntegrationTests/BaseIntegrationTest.cs +++ b/src/tests/IntegrationTests/BaseIntegrationTest.cs @@ -62,11 +62,14 @@ protected void InitializeEnvironment(NPath repoPath, cacheContainer.SetCacheInitializer(CacheType.GitUser, () => GitUserCache.Instance); cacheContainer.SetCacheInitializer(CacheType.RepositoryInfo, () => RepositoryInfoCache.Instance); - Environment = new IntegrationTestEnvironment(cacheContainer, - repoPath, - SolutionDirectory, - enableTrace: enableEnvironmentTrace, - initializeRepository: initializeRepository); + var environment = new IntegrationTestEnvironment(cacheContainer, + repoPath, + SolutionDirectory, + enableTrace: enableEnvironmentTrace, + initializeRepository: initializeRepository); + environment.NodeJsExecutablePath = TestApp; + environment.OctorunScriptPath = TestApp; + Environment = environment; } protected void InitializePlatform(NPath repoPath, diff --git a/src/tests/IntegrationTests/IntegrationTestEnvironment.cs b/src/tests/IntegrationTests/IntegrationTestEnvironment.cs index f85a7b227..004864ab7 100644 --- a/src/tests/IntegrationTests/IntegrationTestEnvironment.cs +++ b/src/tests/IntegrationTests/IntegrationTestEnvironment.cs @@ -100,7 +100,7 @@ public string GetSpecialFolder(Environment.SpecialFolder folder) public NPath GitLfsExecutablePath => defaultEnvironment.GitLfsExecutablePath; public GitInstaller.GitInstallationState GitInstallationState { get { return defaultEnvironment.GitInstallationState; } set { defaultEnvironment.GitInstallationState = value; } } - public NPath NodeJsExecutablePath => defaultEnvironment.NodeJsExecutablePath; + public NPath NodeJsExecutablePath { get; set; } public NPath OctorunScriptPath { get; set; } diff --git a/src/tests/IntegrationTests/Metrics/MetricsTests.cs b/src/tests/IntegrationTests/Metrics/MetricsTests.cs index 3d7fa1bdb..34aaa42ad 100644 --- a/src/tests/IntegrationTests/Metrics/MetricsTests.cs +++ b/src/tests/IntegrationTests/Metrics/MetricsTests.cs @@ -110,7 +110,7 @@ public void SubmissionWorks() storePath.WriteAllText(savedStore.ToJson(lowerCase: true)); settings.Get(Arg.Is(Constants.MetricsKey), Arg.Any()).Returns(true); - var metricsService = new MetricsService(ProcessManager, TaskManager, Environment.FileSystem, TestApp, TestApp); + var metricsService = new MetricsService(ProcessManager, TaskManager, Platform.Keychain, Environment); usageTracker.MetricsService = metricsService; var method = usageTracker.GetType().GetMethod("SendUsage", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); method.Invoke(usageTracker, null); diff --git a/src/tests/UnitTests/Authentication/KeychainTests.cs b/src/tests/UnitTests/Authentication/KeychainTests.cs index 4c5a1e3a0..5e077ef1f 100644 --- a/src/tests/UnitTests/Authentication/KeychainTests.cs +++ b/src/tests/UnitTests/Authentication/KeychainTests.cs @@ -176,7 +176,7 @@ public void ShouldLoadFromConnectionManager() fileSystem.DidNotReceive().WriteAllLines(Args.String, Arg.Any()); var uriString = keychain.Hosts.FirstOrDefault(); - var keychainAdapter = keychain.Load(uriString); + var keychainAdapter = keychain.LoadFromSystem(uriString); keychainAdapter.Credential.Username.Should().Be(username); keychainAdapter.Credential.Token.Should().Be(token); keychainAdapter.Credential.Host.Should().Be(hostUri); @@ -222,7 +222,7 @@ public void ShouldDeleteFromCacheWhenLoadReturnsNullFromConnectionManager() fileSystem.ClearReceivedCalls(); var uriString = keychain.Hosts.FirstOrDefault(); - var keychainAdapter = keychain.Load(uriString); + var keychainAdapter = keychain.LoadFromSystem(uriString); keychainAdapter.Should().BeNull(); fileSystem.DidNotReceive().FileExists(Args.String); @@ -281,21 +281,21 @@ public void ShouldConnectSetCredentialsTokenAndSave() keychainAdapter.Credential.Should().BeNull(); - keychain.SetCredentials(new Credential(hostUri, username, password)); + keychainAdapter.Set(new Credential(hostUri, username, password)); keychainAdapter.Credential.Should().NotBeNull(); keychainAdapter.Credential.Host.Should().Be(hostUri); keychainAdapter.Credential.Username.Should().Be(username); keychainAdapter.Credential.Token.Should().Be(password); - keychain.SetToken(hostUri, token, username); + keychainAdapter.Update(token, username); keychainAdapter.Credential.Should().NotBeNull(); keychainAdapter.Credential.Host.Should().Be(hostUri); keychainAdapter.Credential.Username.Should().Be(username); keychainAdapter.Credential.Token.Should().Be(token); - keychain.Save(hostUri); + keychain.SaveToSystem(hostUri); fileSystem.DidNotReceive().FileExists(Args.String); fileSystem.DidNotReceive().FileDelete(Args.String); @@ -352,7 +352,7 @@ public void ShouldConnectSetCredentialsAndClear() keychainAdapter.Credential.Should().BeNull(); - keychain.SetCredentials(new Credential(hostUri, username, password)); + keychainAdapter.Set(new Credential(hostUri, username, password)); keychainAdapter.Credential.Should().NotBeNull(); keychainAdapter.Credential.Host.Should().Be(hostUri); diff --git a/submodules/packaging b/submodules/packaging new file mode 160000 index 000000000..43af07792 --- /dev/null +++ b/submodules/packaging @@ -0,0 +1 @@ +Subproject commit 43af077928bf3cfa06cfaac507b801efc950d475