From 286512a11f6472943c80d97e2b90014d03c01f18 Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Tue, 6 Apr 2021 15:16:23 +0100 Subject: [PATCH 01/69] Add hx3compat dependency --- haxelib.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/haxelib.json b/haxelib.json index 9da66e387..527508941 100644 --- a/haxelib.json +++ b/haxelib.json @@ -7,5 +7,8 @@ "classPath": "src", "version": "4.0.2", "releasenote": " * Fixed too strict requirements to haxelib.json data for private libs (#484)", - "contributors": ["HaxeFoundation", "back2dos", "ncannasse", "jason", "Simn", "nadako", "andyli"] + "contributors": ["HaxeFoundation", "back2dos", "ncannasse", "jason", "Simn", "nadako", "andyli"], + "dependencies":{ + "hx3compat":"git:https://github.com/haxefoundation/hx3compat.git#f1f18201e5c0479cb5adf5f6028788b37f37b730" + } } From b3c83083724e60aba6f0807451a7a5297a62c3e5 Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Tue, 6 Apr 2021 15:24:32 +0100 Subject: [PATCH 02/69] Move functions to FsUtils - Move IS_WINDOWS - Remove extra absolutePath implementation --- src/haxelib/client/FsUtils.hx | 24 +++++++++++++++++++++-- src/haxelib/client/Main.hx | 36 +++-------------------------------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/src/haxelib/client/FsUtils.hx b/src/haxelib/client/FsUtils.hx index 6cbeed825..3236512fb 100644 --- a/src/haxelib/client/FsUtils.hx +++ b/src/haxelib/client/FsUtils.hx @@ -28,8 +28,7 @@ using StringTools; /** Class containing useful FileSystem utility functions. **/ @:noDoc class FsUtils { - static var IS_WINDOWS = (Sys.systemName() == "Windows"); - + public static final IS_WINDOWS = (Sys.systemName() == "Windows"); /** Recursively follow symlink @@ -140,4 +139,25 @@ class FsUtils { try FileSystem.fullPath(path) catch (error:String) if (error == "std@file_full_path") errors++; return errors == 2; } + + public static function getHomePath():String { + var home:String = null; + if (IS_WINDOWS) { + home = Sys.getEnv("USERPROFILE"); + if (home == null) { + final drive = Sys.getEnv("HOMEDRIVE"); + final path = Sys.getEnv("HOMEPATH"); + if (drive != null && path != null) + home = drive + path; + } + if (home == null) + throw "Could not determine home path. Please ensure that USERPROFILE or HOMEDRIVE+HOMEPATH environment variables are set."; + } else { + home = Sys.getEnv("HOME"); + if (home == null) + throw "Could not determine home path. Please ensure that HOME environment variable is set."; + } + return home; + } + } diff --git a/src/haxelib/client/Main.hx b/src/haxelib/client/Main.hx index 0c1c9d6ff..dca2b01b8 100644 --- a/src/haxelib/client/Main.hx +++ b/src/haxelib/client/Main.hx @@ -182,7 +182,6 @@ class Main { apiVersion : "3.0", noSsl : false }; - static final IS_WINDOWS = (Sys.systemName() == "Windows"); final commands:List<{name:String, doc:String, f:Void->Void, net:Bool, cat:CommandCategory}>; final isHaxelibRun:Bool; @@ -1137,26 +1136,6 @@ class Main { } } - static public function getHomePath():String{ - var home:String = null; - if (IS_WINDOWS) { - home = Sys.getEnv("USERPROFILE"); - if (home == null) { - final drive = Sys.getEnv("HOMEDRIVE"); - final path = Sys.getEnv("HOMEPATH"); - if (drive != null && path != null) - home = drive + path; - } - if (home == null) - throw "Could not determine home path. Please ensure that USERPROFILE or HOMEDRIVE+HOMEPATH environment variables are set."; - } else { - home = Sys.getEnv("HOME"); - if (home == null) - throw "Could not determine home path. Please ensure that HOME environment variable is set."; - } - return home; - } - static public function getConfigFile():String { return Path.addTrailingSlash( getHomePath() ) + ".haxelib"; } @@ -1268,8 +1247,7 @@ class Main { rep = line; } - - rep = try absolutePath(rep) catch (e:Dynamic) rep; + rep = try FileSystem.absolutePath(rep) catch (e:Dynamic) rep; if (isSamePath(rep, configFile)) throw "Can't use "+rep+" because it is reserved for config file"; @@ -1833,7 +1811,7 @@ class Main { } function newRepo() { - final path = absolutePath(REPODIR); + final path = FileSystem.absolutePath(REPODIR); final created = FsUtils.safeDir(path, true); if (created) print('Local repository created ($path)'); @@ -1842,7 +1820,7 @@ class Main { } function deleteRepo() { - final path = absolutePath(REPODIR); + final path = FileSystem.absolutePath(REPODIR); final deleted = FsUtils.deleteRec(path); if (deleted) print('Local repository deleted ($path)'); @@ -1874,14 +1852,6 @@ class Main { } } - // haxe 3.1.3 doesn't have FileSystem.absolutePath() - static function absolutePath(path:String) { - if (StringTools.startsWith(path, '/') || path.charAt(1) == ':' || StringTools.startsWith(path, '\\\\')) { - return path; - } - return haxe.io.Path.join([Sys.getCwd(), path]); - } - // deprecated commands function local() { doInstallFile(getRepository(), param("Package"), true, true); From 5be6aa77916a2f01567bf770fec95c66889b054e Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Tue, 6 Apr 2021 15:27:26 +0100 Subject: [PATCH 03/69] Extract repo management methods to new module Make minor refactor of `setup` command code --- src/haxelib/client/FsUtils.hx | 9 ++ src/haxelib/client/Main.hx | 159 +++++-------------------- src/haxelib/client/RepoManager.hx | 192 ++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 128 deletions(-) create mode 100644 src/haxelib/client/RepoManager.hx diff --git a/src/haxelib/client/FsUtils.hx b/src/haxelib/client/FsUtils.hx index 3236512fb..563885492 100644 --- a/src/haxelib/client/FsUtils.hx +++ b/src/haxelib/client/FsUtils.hx @@ -160,4 +160,13 @@ class FsUtils { return home; } + /** Returns absolute path, replacing `~` with homepath **/ + public static function getFullPath(path:String):String { + final splitPath = path.split("/"); + if (splitPath.length != 0 && splitPath.shift() == "~") { + return getHomePath() + "/" + splitPath.join("/"); + } + + return FileSystem.absolutePath(path); + } } diff --git a/src/haxelib/client/Main.hx b/src/haxelib/client/Main.hx index dca2b01b8..c390c5848 100644 --- a/src/haxelib/client/Main.hx +++ b/src/haxelib/client/Main.hx @@ -38,6 +38,7 @@ import haxelib.client.Vcs; import haxelib.client.Util.*; import haxelib.client.FsUtils.*; import haxelib.client.Cli.ask; +import haxelib.client.RepoManager.RepoException; using StringTools; using Lambda; @@ -171,8 +172,6 @@ class Main { static final VERSION:SemVer = SemVer.ofString(getHaxelibVersion()); static final VERSION_LONG:String = getHaxelibVersionLong(); - static final REPNAME = "lib"; - static final REPODIR = ".haxelib"; static final SERVER = { protocol : "https", host : "lib.haxe.org", @@ -469,7 +468,7 @@ class Main { } if (!isHaxelibRun && !settings.system) { - final rep = try getGlobalRepository() catch (_:Dynamic) null; + final rep = try RepoManager.getGlobalPath() catch (_:Dynamic) null; if (rep != null && FileSystem.exists(rep + HAXELIB_LIBNAME)) { argcur = 0; // send all arguments try { @@ -517,6 +516,16 @@ class Main { checkUpdate(); } c.f(); + } catch (e:RepoManager.InvalidConfiguration) { + switch e.type { + case NoneSet: + print('Error: This is the first time you are running haxelib. Please run `haxelib setup` first.'); + case NotFound(_): + print('Error: ${e.message}. Please run `haxelib setup` again.'); + case IsFile(_): + print('Error: ${e.message}. Please remove it and run `haxelib setup` again.'); + } + Sys.exit(1); } catch( e : Dynamic ) { if( e == "std@host_resolve" ) { print("Host "+SERVER.host+" was not found"); @@ -1136,126 +1145,26 @@ class Main { } } - static public function getConfigFile():String { - return Path.addTrailingSlash( getHomePath() ) + ".haxelib"; - } - - function getGlobalRepositoryPath(create = false):String { - // first check the env var - var rep = Sys.getEnv("HAXELIB_PATH"); - if (rep != null) - return rep.trim(); - - // try to read from user config - rep = try File.getContent(getConfigFile()).trim() catch (_:Dynamic) null; - if (rep != null) - return rep; - - if (!IS_WINDOWS) { - // on unixes, try to read system-wide config - rep = try File.getContent("/etc/.haxelib").trim() catch (_:Dynamic) null; - if (rep == null) - throw "This is the first time you are running haxelib. Please run `haxelib setup` first"; - } else { - // on windows, try to use haxe installation path - rep = getWindowsDefaultGlobalRepositoryPath(); - if (create) - try safeDir(rep) catch(e:Dynamic) throw 'Error accessing Haxelib repository: $e'; - } - - return rep; - } - - // The Windows haxe installer will setup %HAXEPATH%. We will default haxelib repo to %HAXEPATH%/lib. - // When there is no %HAXEPATH%, we will use a "haxelib" directory next to the config file, ".haxelib". - function getWindowsDefaultGlobalRepositoryPath():String { - final haxepath = Sys.getEnv("HAXEPATH"); - if (haxepath != null) - return Path.addTrailingSlash(haxepath.trim()) + REPNAME; - else - return Path.join([Path.directory(getConfigFile()), "haxelib"]); - } - - function getSuggestedGlobalRepositoryPath():String { - if (IS_WINDOWS) - return getWindowsDefaultGlobalRepositoryPath(); - - return if (FileSystem.exists("/usr/share/haxe")) // for Debian - '/usr/share/haxe/$REPNAME' - else if (Sys.systemName() == "Mac") // for newer OSX, where /usr/lib is not writable - '/usr/local/lib/haxe/$REPNAME' - else - '/usr/lib/haxe/$REPNAME'; // for other unixes - } - function getRepository():String { - if (!settings.global) - return switch getLocalRepository() { - case null: getGlobalRepository(); - case repo: Path.addTrailingSlash(FileSystem.fullPath(repo)); - } - else - return getGlobalRepository(); - } - - function getLocalRepository():Null { - var dir = Path.removeTrailingSlashes(Sys.getCwd()); - while (dir != null) { - final repo = Path.addTrailingSlash(dir) + REPODIR; - if(FileSystem.exists(repo) && FileSystem.isDirectory(repo)) { - return repo; - } else { - dir = new Path(dir).dir; - } - } - return null; - } - - function getGlobalRepository():String { - final rep = getGlobalRepositoryPath(true); - if (!FileSystem.exists(rep)) - throw "haxelib Repository " + rep + " does not exist. Please run `haxelib setup` again."; - else if (!FileSystem.isDirectory(rep)) - throw "haxelib Repository " + rep + " exists, but is a file, not a directory. Please remove it and run `haxelib setup` again."; - return Path.addTrailingSlash(rep); + if (settings.global) + return RepoManager.getGlobalPath(); + return RepoManager.getPath(); } function setup() { - var rep = try getGlobalRepositoryPath() catch (_:Dynamic) null; + final suggested = RepoManager.suggestGlobalPath(); - final configFile = getConfigFile(); + final prompt = 'Please enter haxelib repository path with write access\n' + + 'Hit enter for default ($suggested)\n' + + 'Path'; - if (args.length <= argcur) { - if (rep == null) - rep = getSuggestedGlobalRepositoryPath(); - print("Please enter haxelib repository path with write access"); - print("Hit enter for default (" + rep + ")"); - } + final input = param(prompt); - var line = param("Path"); - if (line != "") { - final splitLine = line.split("/"); - if(splitLine[0] == "~") { - var home = getHomePath(); + final path = if (input != "") FsUtils.getFullPath(input) else suggested; - for(i in 1...splitLine.length) { - home += "/" + splitLine[i]; - } - line = home; - } - - rep = line; - } - - rep = try FileSystem.absolutePath(rep) catch (e:Dynamic) rep; + RepoManager.setGlobalPath(path); - if (isSamePath(rep, configFile)) - throw "Can't use "+rep+" because it is reserved for config file"; - - safeDir(rep); - File.saveContent(configFile, rep); - - print("haxelib repository is now " + rep); + print("haxelib repository is now " + path); } function config() { @@ -1811,26 +1720,20 @@ class Main { } function newRepo() { - final path = FileSystem.absolutePath(REPODIR); - final created = FsUtils.safeDir(path, true); - if (created) - print('Local repository created ($path)'); - else - print('Local repository already exists ($path)'); + RepoManager.createLocal(); + final path = RepoManager.getPath(); + print('Local repository created ($path)'); } function deleteRepo() { - final path = FileSystem.absolutePath(REPODIR); - final deleted = FsUtils.deleteRec(path); - if (deleted) - print('Local repository deleted ($path)'); - else - print('No local repository found ($path)'); + final path = RepoManager.getPath(); + RepoManager.deleteLocal(); + print('Local repository deleted ($path)'); } // ---------------------------------- - inline function print(str) + public static inline function print(str) Sys.println(str); static function main() { @@ -1858,6 +1761,6 @@ class Main { } function updateSelf() { - updateByName(getGlobalRepository(), HAXELIB_LIBNAME); + updateByName(RepoManager.getGlobalPath(), HAXELIB_LIBNAME); } } diff --git a/src/haxelib/client/RepoManager.hx b/src/haxelib/client/RepoManager.hx new file mode 100644 index 000000000..3260b190c --- /dev/null +++ b/src/haxelib/client/RepoManager.hx @@ -0,0 +1,192 @@ +package haxelib.client; + +import sys.FileSystem; +import sys.io.File; + +import haxelib.client.FsUtils.*; + +using StringTools; +using haxe.io.Path; + +class RepoException extends haxe.Exception {} + +enum InvalidConfigurationType { + NoneSet; + NotFound(path:String); + IsFile(path:String); +} + +class InvalidConfiguration extends RepoException { + public final type:InvalidConfigurationType; + public function new(type:InvalidConfigurationType) { + final message = switch type { + case NoneSet: "No global repository has been configured"; + case NotFound(path): 'Haxelib repository $path does not exist'; + case IsFile(path): 'Haxelib repository $path exists, but is a file, not a directory'; + } + super(message); + this.type = type; + } + +} + +class RepoManager { + static final REPO_DIR = "lib"; + static final LOCAL_REPO_DIR = ".haxelib"; + + static final CONFIG_FILE = ".haxelib"; + static final UNIX_SYSTEM_CONFIG_FILE = "/etc/.haxelib"; + + static final VARIABLE_NAME = "HAXELIB_PATH"; + + public static function getPath(?dir:String):String { + final dir = getDirectory(dir); + + final localPath = getLocalPath(dir); + if (localPath != null) + return localPath; + + return getValidGlobalPath(); + } + + static function getLocalPath(dir:String):Null { + if (dir == "") + return null; + final repo = Path.join([dir, LOCAL_REPO_DIR]); + if (FileSystem.exists(repo) && FileSystem.isDirectory(repo)) + return FileSystem.fullPath(repo).addTrailingSlash(); + return getLocalPath(dir.directory()); + } + + public static function getGlobalPath():String { + return getValidGlobalPath(); + } + + static function getValidGlobalPath():String { + final rep = readConfiguredGlobalPath(); + if (rep == null) { + if (!IS_WINDOWS) + throw new InvalidConfiguration(NoneSet); + // on Windows, we use the default one if none is set + final defaultPath = getDefaultGlobalPath(); + try + safeDir(defaultPath) + catch (e:Dynamic) + throw new RepoException('Error accessing Haxelib repository: $e'); + // configure the default as the global repository + File.saveContent(getConfigFilePath(), defaultPath); + return defaultPath; + } + if (!FileSystem.exists(rep)) + throw new InvalidConfiguration(NotFound(rep)); + else if (!FileSystem.isDirectory(rep)) + throw new InvalidConfiguration(IsFile(rep)); + return rep; + } + + public static function setGlobalPath(path:String):Void { + path = FileSystem.absolutePath(path); + final configFile = getConfigFilePath(); + + if (isSamePath(path, configFile)) + throw new RepoException('Cannot use $path because it is reserved for config file'); + + safeDir(path); + File.saveContent(configFile, path); + } + + public static function unsetGlobalPath():Void { + final configFile = getConfigFilePath(); + FileSystem.deleteFile(configFile); + } + + public static function suggestGlobalPath() { + final configured = readConfiguredGlobalPath(); + if (configured != null) + return configured; + + return getDefaultGlobalPath(); + } + + static function readConfiguredGlobalPath():Null { + // first check the env var + final environmentVar = Sys.getEnv(VARIABLE_NAME); + if (environmentVar != null) + return environmentVar.trim().addTrailingSlash(); + + // try to read from user config + try { + return getTrimmedContent(getConfigFilePath()).addTrailingSlash(); + } catch (_) { + // the code below could go in here instead, but that + // results in extra nesting... + } + + if (!IS_WINDOWS) { + // on unixes, try to read system-wide config + /* TODO the system wide config has never been configured in haxelib code. + Either configure it somewhere or remove this bit of code? */ + try { + return getTrimmedContent(UNIX_SYSTEM_CONFIG_FILE).addTrailingSlash(); + } catch (_) {} + } + return null; + } + + public static function createLocal(?dir:String) { + if (! (dir == null || FileSystem.exists(dir))) + FsUtils.safeDir(dir); + final dir = getDirectory(dir); + final path = FileSystem.absolutePath(Path.join([dir, LOCAL_REPO_DIR])); + final created = FsUtils.safeDir(path, true); + if(!created) + throw new RepoException('Local repository already exists ($path)'); + } + + public static function deleteLocal(?dir:String) { + final dir = getDirectory(dir); + final path = FileSystem.absolutePath(Path.join([dir, LOCAL_REPO_DIR])); + final deleted = FsUtils.deleteRec(path); + if (!deleted) + throw new RepoException('No local repository found ($path)'); + } + + static function getConfigFilePath():String { + return Path.join([getHomePath(), CONFIG_FILE]); + } + + static function getDefaultGlobalPath():String { + if (IS_WINDOWS) + return getWindowsDefaultGlobalPath(); + + // TODO `lib/` is for binaries, see if we can move all of these to `share/` + return if (FileSystem.exists("/usr/share/haxe/")) // for Debian + '/usr/share/haxe/$REPO_DIR/' + else if (Sys.systemName() == "Mac") // for newer OSX, where /usr/lib is not writable + '/usr/local/lib/haxe/$REPO_DIR/' + else '/usr/lib/haxe/$REPO_DIR/'; // for other unixes + } + + static function getWindowsDefaultGlobalPath():String { + final haxepath = Sys.getEnv("HAXEPATH"); + if (haxepath != null) + return Path.join([haxepath.trim(), REPO_DIR]).addTrailingSlash(); + return Path.join([getConfigFilePath().directory(), "haxelib"]).addTrailingSlash(); + } + + static function getTrimmedContent(filePath:String):String { + return File.getContent(filePath).trim(); + } + + static function getDirectory(dir:Null):String { + if (dir == null) + return Sys.getCwd(); + return Path.addTrailingSlash( + try { + FileSystem.fullPath(dir); + } catch (e) { + throw '$dir does not exist'; + } + ); + } +} From 7b1d4b0e6753d1b4ae5aa21b02e59ac4bbe629ef Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Tue, 6 Apr 2021 15:47:37 +0100 Subject: [PATCH 04/69] Add documentation to RepoManager module --- src/haxelib/client/RepoManager.hx | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/haxelib/client/RepoManager.hx b/src/haxelib/client/RepoManager.hx index 3260b190c..a4f4a3de5 100644 --- a/src/haxelib/client/RepoManager.hx +++ b/src/haxelib/client/RepoManager.hx @@ -11,8 +11,11 @@ using haxe.io.Path; class RepoException extends haxe.Exception {} enum InvalidConfigurationType { + /** There is no configuration set **/ NoneSet; + /** The configured folder does not exist **/ NotFound(path:String); + /** The configuration points to a file instead of a directory **/ IsFile(path:String); } @@ -30,6 +33,7 @@ class InvalidConfiguration extends RepoException { } +/** Manager for the location of the haxelib database. **/ class RepoManager { static final REPO_DIR = "lib"; static final LOCAL_REPO_DIR = ".haxelib"; @@ -39,6 +43,12 @@ class RepoManager { static final VARIABLE_NAME = "HAXELIB_PATH"; + /** + Returns the path to the repository local to `dir` if one exists, + otherwise returns global repository path. + + If `dir` is omitted, the current working directory is used instead. + **/ public static function getPath(?dir:String):String { final dir = getDirectory(dir); @@ -49,6 +59,12 @@ class RepoManager { return getValidGlobalPath(); } + /** + Searches for the path to local repository, starting in `dir` + and then going up until root directory is reached. + + Returns the directory path if it is found, otherwise returns null. + **/ static function getLocalPath(dir:String):Null { if (dir == "") return null; @@ -58,6 +74,13 @@ class RepoManager { return getLocalPath(dir.directory()); } + /** + Returns the global repository path, but throws an exception + if it does not exist or if it is not a directory. + + The `HAXELIB_PATH` environment variable takes precedence over + the configured global repository path. + **/ public static function getGlobalPath():String { return getValidGlobalPath(); } @@ -84,6 +107,11 @@ class RepoManager { return rep; } + /** + Sets `path` as the global haxelib repository in the user's haxelib config file. + + If `path` does not exist already, it is created. + **/ public static function setGlobalPath(path:String):Void { path = FileSystem.absolutePath(path); final configFile = getConfigFilePath(); @@ -95,11 +123,20 @@ class RepoManager { File.saveContent(configFile, path); } + /** + Deletes the user's current haxelib setup, + resetting their global repository path. + **/ public static function unsetGlobalPath():Void { final configFile = getConfigFilePath(); FileSystem.deleteFile(configFile); } + /** + Returns the previous global repository path if a valid one had been + set up, otherwise returns the default path for the current operating + system. + **/ public static function suggestGlobalPath() { final configured = readConfiguredGlobalPath(); if (configured != null) @@ -108,6 +145,16 @@ class RepoManager { return getDefaultGlobalPath(); } + /** + Returns the global Haxelib repository path, without validating + that it exists. If it is not configured anywhere, returns `null`. + + First checks `HAXELIB_PATH` environment variable, + then checks the content of user config file. + + On Unix-like systems also checks `/etc/.haxelib` for system wide + configuration. + **/ static function readConfiguredGlobalPath():Null { // first check the env var final environmentVar = Sys.getEnv(VARIABLE_NAME); @@ -133,6 +180,13 @@ class RepoManager { return null; } + /** + Creates a new local repository in the directory `dir` if one doesn't already exist. + + If `dir` is ommited, the current working directory is used. + + Throws RepoException if repository already exists. + **/ public static function createLocal(?dir:String) { if (! (dir == null || FileSystem.exists(dir))) FsUtils.safeDir(dir); @@ -143,6 +197,13 @@ class RepoManager { throw new RepoException('Local repository already exists ($path)'); } + /** + Deletes the local repository in the directory `dir`, if it exists. + + If `dir` is ommited, the current working directory is used. + + Throws RepoException if no repository is found. + **/ public static function deleteLocal(?dir:String) { final dir = getDirectory(dir); final path = FileSystem.absolutePath(Path.join([dir, LOCAL_REPO_DIR])); @@ -155,6 +216,7 @@ class RepoManager { return Path.join([getHomePath(), CONFIG_FILE]); } + /** Returns the default path for the global directory. **/ static function getDefaultGlobalPath():String { if (IS_WINDOWS) return getWindowsDefaultGlobalPath(); @@ -167,6 +229,13 @@ class RepoManager { else '/usr/lib/haxe/$REPO_DIR/'; // for other unixes } + /** + The Windows haxe installer will setup `%HAXEPATH%`. + We will default haxelib repo to `%HAXEPATH%/lib.` + + When there is no `%HAXEPATH%`, we will use a `/haxelib` + directory next to the config file, ".haxelib". + **/ static function getWindowsDefaultGlobalPath():String { final haxepath = Sys.getEnv("HAXEPATH"); if (haxepath != null) From 78391222cc8ebd19989a458f1db9a75813107a57 Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Tue, 6 Apr 2021 23:04:23 +0100 Subject: [PATCH 05/69] Error if haxe version doesn't have haxe.Exception --- src/haxelib/client/RepoManager.hx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/haxelib/client/RepoManager.hx b/src/haxelib/client/RepoManager.hx index a4f4a3de5..fbc36cb58 100644 --- a/src/haxelib/client/RepoManager.hx +++ b/src/haxelib/client/RepoManager.hx @@ -8,6 +8,10 @@ import haxelib.client.FsUtils.*; using StringTools; using haxe.io.Path; +#if (haxe_ver < 4.1) +#error "RepoManager requires Haxe 4.1 or newer" +#end + class RepoException extends haxe.Exception {} enum InvalidConfigurationType { From 77e8c5b56805f584c453e07861edbb36501733d2 Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Sun, 18 Apr 2021 21:48:42 +0100 Subject: [PATCH 06/69] [tests] Add tests for RepoManager module --- test/HaxelibTests.hx | 3 +- test/tests/TestRepoManager.hx | 236 ++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 test/tests/TestRepoManager.hx diff --git a/test/HaxelibTests.hx b/test/HaxelibTests.hx index 19ce09d1b..7bc36e6a0 100644 --- a/test/HaxelibTests.hx +++ b/test/HaxelibTests.hx @@ -70,8 +70,9 @@ class HaxelibTests { r.add(new TestVcsNotFound()); r.add(new TestInstall()); + r.add(new TestRepoManager()); final success = r.run(); Sys.exit(success ? 0 : 1); } -} \ No newline at end of file +} diff --git a/test/tests/TestRepoManager.hx b/test/tests/TestRepoManager.hx new file mode 100644 index 000000000..f9b2dfccc --- /dev/null +++ b/test/tests/TestRepoManager.hx @@ -0,0 +1,236 @@ +package tests; + +import sys.io.File; +import sys.FileSystem; + +import haxelib.client.RepoManager; + +using haxe.io.Path; + +class TestRepoManager extends TestBase { + static final REPO = "haxelib-repo"; + static final LOCAL_REPO = ".haxelib/"; + + final repo = Path.join([Sys.getCwd(), "test", REPO]).addTrailingSlash(); + var origRepo:String = null; + var cwd:String = null; + + // Setup and teardown + + override public function setup():Void { + cwd = Sys.getCwd(); + Sys.setCwd("test"); + + origRepo = ~/\r?\n/.split(runHaxelib(["config"]).stdout)[0].normalize(); + + if (runHaxelib(["setup", repo]).exitCode != 0) + throw "haxelib setup failed"; + } + + override public function tearDown():Void { + if (runHaxelib(["setup", origRepo]).exitCode != 0) { + throw "haxelib setup failed"; + } + if (FileSystem.exists(LOCAL_REPO)) + deleteDirectory(LOCAL_REPO); + if (FileSystem.exists("tmp")) + deleteDirectory("tmp"); + + deleteDirectory(repo); + Sys.setCwd(cwd); + } + + // Tests + + public function testNewRepo() { + RepoManager.createLocal(); + assertTrue(FileSystem.exists(LOCAL_REPO)); + + // throws error if one already exists + try { + RepoManager.createLocal(); + assertFalse(true); + } catch(e:RepoException) { + assertTrue(true); + } + + deleteDirectory(LOCAL_REPO); + + // relative path + final tmp = "tmp/"; + + RepoManager.createLocal(tmp); + assertTrue(FileSystem.exists(tmp + LOCAL_REPO)); + deleteDirectory(tmp + LOCAL_REPO); + + // absolute path + final tmp = Sys.getCwd() + "tmp/"; + + RepoManager.createLocal(tmp); + assertTrue(FileSystem.exists(tmp + LOCAL_REPO)); + deleteDirectory(tmp + LOCAL_REPO); + } + + public function testDeleteRepo() { + FileSystem.createDirectory(LOCAL_REPO); + + RepoManager.deleteLocal(); + assertFalse(FileSystem.exists(LOCAL_REPO)); + + // throws error if no repository exists + try { + RepoManager.deleteLocal(); + assertFalse(true); + } catch (e:RepoException) { + assertTrue(true); + } + + // relative path + final tmp = "tmp/"; + + FileSystem.createDirectory(tmp + LOCAL_REPO); + RepoManager.deleteLocal(tmp); + assertFalse(FileSystem.exists(tmp + LOCAL_REPO)); + + // absolute path + final tmp = Sys.getCwd() + "tmp/"; + + FileSystem.createDirectory(tmp + LOCAL_REPO); + RepoManager.deleteLocal(tmp); + assertFalse(FileSystem.exists(tmp + LOCAL_REPO)); + } + + public function testFindRepository() { + // local repo exists + RepoManager.createLocal(); + + assertEquals( + FileSystem.absolutePath(LOCAL_REPO), + RepoManager.getPath().normalize() + ); + + final tmp = "tmp/"; + FileSystem.createDirectory(tmp); + + // relative path + FileSystem.createDirectory(LOCAL_REPO); + assertEquals( + FileSystem.absolutePath(LOCAL_REPO), + RepoManager.getPath(tmp).normalize() + ); + + // absolute path + assertEquals( + FileSystem.absolutePath(LOCAL_REPO), + RepoManager.getPath(Sys.getCwd() + tmp).normalize() + ); + + + // no local repo exists, should go to global + RepoManager.deleteLocal(); + + assertEquals(repo, RepoManager.getPath()); + + // relative path + assertEquals(repo, RepoManager.getPath(tmp)); + + // absolute path + assertEquals(repo, RepoManager.getPath(Sys.getCwd() + tmp)); + } + + public function testGlobalRepository() { + // test current setup + assertEquals(repo, RepoManager.getGlobalPath()); + + // test enrivonment variable + final cwd = Sys.getCwd(); + Sys.putEnv("HAXELIB_PATH", cwd); + assertEquals(cwd, RepoManager.getGlobalPath()); + // empty it + Sys.putEnv("HAXELIB_PATH", null); + } + + public function testInvalidGlobalRepositories(){ + function isInvalid() { + return try { + RepoManager.getGlobalPath(); + false; + } catch (e:RepoException) { + true; + } + } + + /* to non existent folder */ + + RepoManager.setGlobalPath("toDelete"); + FileSystem.deleteDirectory("toDelete"); + assertTrue(isInvalid()); + + /* to a file */ + + RepoManager.setGlobalPath("toDelete"); + FileSystem.deleteDirectory("toDelete"); + + // create the file + File.saveContent("toDelete", ""); + assertTrue(isInvalid()); + + // clean up + FileSystem.deleteFile("toDelete"); + + /* no global repository set */ + + RepoManager.unsetGlobalPath(); + + if (Sys.systemName() == "Windows") { + // on windows, should provide the default value instead of the old set one + final newValue = RepoManager.getGlobalPath(); + assertFalse(repo == newValue); + } else { + // on unix throw an error if no path is set + // TODO: unless /etc/.haxelib/ is set + try { + RepoManager.getGlobalPath(); + assertFalse(true); + } catch (e:RepoException) { + assertTrue(true); + } + } + + RepoManager.setGlobalPath(repo); + } + + public function testSuggestGlobalRepositoryPath() { + // if one is set already, suggest it + assertEquals(repo, RepoManager.suggestGlobalPath()); + + // if none is set, give platform specific default + // TODO: Test each individual platform?? + RepoManager.unsetGlobalPath(); + + // should not give the one that was set + assertFalse(repo == RepoManager.suggestGlobalPath()); + + + RepoManager.setGlobalPath(repo); + } + + public function testSetup() { + // clearing it + RepoManager.unsetGlobalPath(); + final newValue = try RepoManager.getGlobalPath() catch(e:RepoException) null; + + assertFalse(repo == newValue); + + // setting it + RepoManager.setGlobalPath(repo); + assertEquals(repo, RepoManager.getGlobalPath()); + + // setting it to relative path + final tmp = "tmp"; + + RepoManager.setGlobalPath(tmp); + assertEquals(FileSystem.absolutePath(tmp).addTrailingSlash(), RepoManager.getGlobalPath()); + } + +} From a83d3c976b295cd79d7e520fc5a89b04023413e6 Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Sun, 18 Apr 2021 21:43:40 +0100 Subject: [PATCH 07/69] [tests] Fix integration test --- test/tests/integration/TestSetup.hx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/tests/integration/TestSetup.hx b/test/tests/integration/TestSetup.hx index d5acca7ac..a08037992 100644 --- a/test/tests/integration/TestSetup.hx +++ b/test/tests/integration/TestSetup.hx @@ -1,12 +1,11 @@ package tests.integration; -import haxelib.client.Main; +import haxelib.client.RepoManager; class TestSetup extends IntegrationTests { function testCleanEnv():Void { // remove .haxelib to simulate an enviroment that haven't `haxelib setup` yet - final config = Main.getConfigFile(); - sys.FileSystem.deleteFile(config); + RepoManager.unsetGlobalPath(); final installResult = haxelib(["setup", originalRepo]).result(); assertEquals(0, installResult.code); From 891a18378f2fb240da7cf582f53cb95c1883bc69 Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Tue, 13 Apr 2021 12:07:11 +0100 Subject: [PATCH 08/69] Refactor client initialization - Set all members of the client main class in the constructor. - Do not store settings that are not necessary outside the constructor. - Do not initialize site multiple times in some cases - Consider --debug and --quiet mutually exclusive - Print switch information in an ordered way - Do not run any code until it is known that the call is not being passed on to the updated haxelib version - Fix -cwd not working when redirecting to haxelib version - Separate command functionality from docs --- src/haxelib/client/Args.hx | 310 ++++++++++++++ src/haxelib/client/Main.hx | 822 ++++++++++++++++++------------------- src/haxelib/client/Util.hx | 30 +- 3 files changed, 738 insertions(+), 424 deletions(-) create mode 100644 src/haxelib/client/Args.hx diff --git a/src/haxelib/client/Args.hx b/src/haxelib/client/Args.hx new file mode 100644 index 000000000..44a478ea0 --- /dev/null +++ b/src/haxelib/client/Args.hx @@ -0,0 +1,310 @@ +package haxelib.client; + +using StringTools; + +class SwitchError extends haxe.Exception {} +class InvalidCommand extends haxe.Exception {} + +@:structInit +class ArgsInfo { + public final command:Command; + public final mainArgs: Array; + public final flags: Array; + public final options: Map; + public final repeatedOptions: Map>; +} + +enum CommandCategory { + Basic; + Information; + Development; + Miscellaneous; +} + +enum abstract Command(String) to String { + final Install = "install"; + final Update = "update"; + final Remove = "remove"; + final List = "list"; + final Set = "set"; + + final Search = "search"; + final Info = "info"; + final User = "user"; + final Config = "config"; + final Path = "path"; + final LibPath = "libpath"; + final Version = "version"; + final Help = "help"; + + final Submit = "submit"; + final Register = "register"; + final Dev = "dev"; + final Git = "git"; + final Hg = "hg"; + + final Setup = "setup"; + final NewRepo = "newrepo"; + final DeleteRepo = "deleterepo"; + final FixRepo = "fixrepo"; + final ConvertXml = "convertxml"; + final Run = "run"; + final Proxy = "proxy"; + // deprecated commands + final Local = "local"; + final SelfUpdate = "selfupdate"; + + static final ALIASES = [ + "upgrade" => Update + ]; + + static final COMMANDS = Util.getValues(Command); + + public static function ofString(str:String):Null { + // first check aliases + final alias = ALIASES.get(str); + if(alias != null) + return alias; + // then check the rest + for (command in COMMANDS) { + if (cast(command, String) == str) + return command; + } + return null; + } + +} + +enum abstract Flag(String) to String { + final Global = "global"; + final Debug = "debug"; + final Quiet = "quiet"; + final Flat = "flat"; + final Always = "always"; + final Never = "never"; + final System = "system"; + final SkipDependencies = "skip-dependencies"; + // hidden + final NoTimeout = "notimeout"; + + public static final MUTUALLY_EXCLUSIVE = [[Quiet, Debug], [Always, Never]]; + + /** + Priority flags that need to be accessed prior to + complete argument parsing. + **/ + public static final PRIORITY = [System, Debug, Global]; + + static final FLAGS = Util.getValues(Flag); + + public static function ofString(str:String):Null { + for (flag in FLAGS) { + if ((flag:String) == str) + return flag; + } + return null; + } + +} + +enum abstract Option(String) to String { + final Remote = "R"; + + static final OPTIONS = Util.getValues(Option); + + public static function ofString(str:String):Null