Files
crosslangextras/Tesses.CrossLang.BuildEssentials/src/PackageManager.tcross

231 lines
8.0 KiB
Plaintext

/^ The CrossLang Package Manager Client ^/
class Tesses.CrossLang.PackageManager
{
private configRoot;
private packageCache;
/^ Work offline (defaults to false) ^/
public Offline = false;
/^ Construct the Package Manager ^/
public PackageManager()
{
this.configRoot = Env.CrossLangConfig;
this.packageCache = this.configRoot / "PackageCache";
FS.Local.CreateDirectory(this.packageCache);
}
/^ Parse package filename ^/
public ParseFileName(name)
{
var index = name.LastIndexOf('.');
if(index == -1) throw "We expect an extension";
if(name.Substring(index) != ".crvm") throw "We expect the extension .crvm";
name = name.Substring(0,index);
//Name-VERSION-[dev|alpha|beta|prod]
var lastIndex = name.LastIndexOf('-');
var verStage = name.Substring(lastIndex+1);
var versionStr = verStage;
if(verStage == "dev" || verStage == "alpha" || verStage == "beta" || verStage == "prod")
{
var lastIndex2 = name.LastIndexOf('-', lastIndex-1);
return {
Name = name.Substring(0,lastIndex2),
Version = name.Substring(lastIndex2+1)
};
}
else
{
return {
Name = name.Substring(0,lastIndex),
Version = name.Substring(lastIndex+1)
};
}
}
/^ Get package servers list ^/
public GetPackageServers()
{
var packageConfigFile = configRoot / "package_servers.json";
if(FS.Local.RegularFileExists(packageConfigFile))
{
return Json.Decode(FS.ReadAllText(FS.Local, packageConfigFile));
}
return ["https://cpkg.tesseslanguage.com/"];
}
/^ Get a package (returns bytearray of package data or null if not found), use GetLatest to get the latest version ^/
public GetPackage(name, version)
{
var v = Version.Parse(version);
var useCache = v.Stage != "dev";
var pkgFile = packageCache / name / v.ToString();
if(useCache && FS.Local.RegularFileExists(pkgFile))
{
return FS.ReadAllBytes(FS.Local,pkgFile);
}
if(this.Offline) return null;
each(var item : this.GetPackageServers())
{
//https://cpkg.tesseslanguage.com/api/v1/download?name=MyPackage&version=1.0.0.0-prod
var uri = $"{item.TrimEnd('/')}/api/v1/download?name={Net.Http.UrlEncode(name)}&version={Net.Http.UrlEncode(version)}";
Console.WriteLine($"Downloading: {name} {version} from {item}");
var req = Net.Http.MakeRequest(uri);
if(req.StatusCode == 200)
{
var strm = new MemoryStream(true);
req.CopyToStream(strm);
if(useCache) {
FS.Local.CreateDirectory(packageCache / name);
var f = FS.Local.OpenFile(pkgFile,"wb");
strm.Seek(0,0);
strm.CopyTo(f);
f.Close();
}
var data = strm.GetBytes();
strm.Close();
req.Close();
return data;
}
}
return null;
}
/^ Get the latest version of a package ^/
public GetLatest(name)
{
var pkgServers = this.GetPackageServers();
if(this.Offline || pkgServers.Count == 0)
{
//user has declared they are offline or don't have packageServers look through packages locally
var version = new Version(0,0,0,0,"dev");
var configRoot = Env.CrossLangConfig;
var dir = configRoot / "PackageCache" / name;
if(FS.Local.DirectoryExists(dir))
each(var f : FS.Local.EnumeratePaths(dir))
{
var v = Version.Parse(f.GetFileName());
if(v >= version)
{
version = v;
}
}
if(version.VersionInt == 0) return null;
return version.ToString();
}
else
{
each(var item : pkgServers)
{
var uri = $"{item.TrimEnd('/')}/api/v1/latest?name={Net.Http.UrlEncode(name)}";
var req = Net.Http.MakeRequest(uri);
if(req.StatusCode == 200)
{
var res0 = req.ReadAsString();
var res = Json.Decode(res0);
req.Close();
return res.version;
}
}
}
return null;
}
/^
Search for packages based on query
Options is a {"server": "SERVER_URL", "type": "lib", "pluginHost": "yourPluginHost","offset":0,"limit": 20}
^/
public Search(query, $options)
{
options = options ?? {};
var urlBase = "/api/v1/search?q={Net.Http.UrlEncode(query)}";
if(TypeOf(options.type) == "String") urlBase += $"&type={Net.Http.UrlEncode(options.type)}";
if(TypeOf(options.pluginHost) == "String") urlBase += "&pluginHost={Net.Http.UrlEncode(options.pluginHost)}";
if(TypeOf(options.offset) == "Long") urlBase += $"&offset={options.offset}";
if(TypeOf(options.limit) == "Long") urlBase += $"&limit={options.limit}";
func handle(server)
{
var req = Net.Http.MakeRequest($"{server.TrimEnd('/')}{urlBase}");
if(req.StatusCode==200)
{
try {
var res2=Json.Decode(req.ReadAsString());
return res2.packages;
}catch(ex) {
}
}
return null;
}
if(TypeOf(options.server) == "String")
return handle(options.server) ?? [];
else {
each(var item : this.GetPackageServers())
{
var r = handle(item);
if(TypeOf(r) == "List") return r;
}
return [];
}
}
/^
Download a plugin to directory
returns true if succeeds or false if it fails
^/
public DownloadPlugin(dirFS, name, version)
{
var pkg = this.GetPackage(name,version.ToString());
if(pkg == null) return false;
var ms = new MemoryStream(true);
ms.WriteBlock(pkg,0,pkg.Count);
ms.Seek(0,0);
var vm = VM.LoadExecutable(ms);
ms.Close();
var info = {};
try{info=Json.Decode(vm.Info);}catch(ex){}
var short_name = TypeOf(info.short_name) == "String" ? info.short_name : name;
dirFS.CreateDirectory(/short_name);
each(var file : dirFS.EnumeratePaths(/short_name))
{
if(file.GetExtension() == ".crvm" && dirFS.FileExists(file))
{
dirFS.DeleteFile(file);
}
}
FS.WriteAllBytes(dirFS,/short_name/$"{short_name}.crvm", pkg);
func dlDeps(_vm)
{
each(var dep : _vm.Dependencies)
{
var path = /short_name/$"{dep.Name}-{dep.Version.ToString()}";
if(!dirFs.RegularFileExists(path))
{
var pkg2 = this.GetPackage(dep.Name,dep.Version.ToString());
if(pkg2 == null) return false;
FS.WriteAllBytes(dirFS,path,pkg2);
var ms2 = new MemoryStream(true);
ms2.WriteBlock(pkg2,0,pkg2.Count);
ms2.Seek(0,0);
var vm2 = VM.LoadExecutable(ms2);
ms2.Close();
dlDeps(vm2);
}
}
return true;
}
return dlDeps(vm);
}
}