Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix haxelib.json issues #554

Merged
merged 11 commits into from
Apr 12, 2022
2 changes: 1 addition & 1 deletion haxelib.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"releasenote": " * Fixed too strict requirements to haxelib.json data for private libs (#484)",
"contributors": ["HaxeFoundation", "back2dos", "ncannasse", "jason", "Simn", "nadako", "andyli"],
"dependencies":{
"hx3compat":"git:https://github.com/haxefoundation/hx3compat.git#f1f18201e5c0479cb5adf5f6028788b37f37b730"
"hx3compat": ""
}
}
158 changes: 63 additions & 95 deletions src/haxelib/Data.hx
Original file line number Diff line number Diff line change
Expand Up @@ -20,76 +20,32 @@
* DEALINGS IN THE SOFTWARE.
*/
package haxelib;
/**
Contains definitions and functions for the data held in the haxelib.json file of a project.
**/

import haxe.ds.Option;
import haxe.ds.*;
import haxe.zip.Reader;
import haxe.zip.Entry;
import haxe.Json;
import haxelib.Validator;
import haxelib.Util;

using StringTools;

/** Information on a `user` in the Haxelib database. **/
typedef UserInfos = {
/** The user's name. **/
var name : String;
/** The user's full name. **/
var fullname : String;
/** The user's email address. **/
var email : String;
/** An array of projects for which the user is a contributor. **/
var projects : Array<String>;
}

/** Information on a specific version of a project in the Haxelib database. **/
typedef VersionInfos = {
/** The release date of the version. **/
var date : String;
/** The version "name" in SemVer form. **/
var name : SemVer;//TODO: this should eventually be called `number`
/** The number of downloads this version of the library has had. **/
var downloads : Int;
/** The release note that came with this release. **/
var comments : String;
}

/** Information on a project in the Haxelib database. **/
typedef ProjectInfos = {
/** The project name. **/
var name : String;
/** The project's description. **/
var desc : String;
/** A link to the project's website. **/
var website : String;
/** The username of the owner of the project. **/
var owner : String;
/** An array of contributor's user names and full names. **/
var contributors : Array<{ name:String, fullname:String }>;
/** The license under which the project is released. **/
var license : String;
/** The current version of the project. **/
var curversion : String;
/** The total number of downloads the project has. **/
var downloads : Int;
/** An array of `VersionInfos` for each release of the library. **/
var versions : Array<VersionInfos>;
/** The project's tags. **/
var tags : List<String>;
}
using Lambda;

/** The level of strictness with which a `haxelib.json` check is performed. **/
@:enum abstract CheckLevel(Int) {
/** No check is performed. **/
var NoCheck = 0;
/** Only the syntax of the file is checked. **/
var CheckSyntax = 1;
/** The syntax is checked and data in the file is validated. **/
var CheckData = 2;
/**
The syntax is checked and data in the file is validated.

@:from static inline function fromBool(check:Bool):CheckLevel {
return check ? CheckData : NoCheck;
}
Data must meet the requirements for publishing to the server.
**/
var CheckData = 2;

@:op(A > B) function gt(b:CheckLevel):Bool;
@:op(A >= B) function gte(b:CheckLevel):Bool;
Expand All @@ -105,14 +61,25 @@ abstract DependencyVersion(String) to String from SemVer {

@:to function toValidatable():Validatable
return
if (this == DEFAULT || this == GIT || (#if (haxe_ver < 4.1) Std.is #else Std.isOfType #end(this, String) && this.startsWith('git:')))
if (this == DEFAULT)
{ validate: function () return None }
else if (#if (haxe_ver < 4.1) Std.is(this, String) #else this is String #end && this.split(":")[0] == GIT )
{validate: function() return Some("Git dependency is not allowed in a library release")}
else
@:privateAccess new SemVer(this);

/** Returns whether `s` constitutes a valid dependency version string. **/
static public function isValid(s:String):Bool
return new DependencyVersion(s).toValidatable().validate() == None;
return s == DEFAULT || SemVer.isValid(s);

/**
Returns whether `s` constitutes dependency version string that can be used locally.

The requirements for this are not as strict as `isValid()`, as vcs
dependencies are allowed.
**/
static public function isUsable(s:String):Bool
return #if (haxe_ver < 4.1) Std.is(s, String) #else (s is String) #end && s.split(":")[0] == GIT || isValid(s);

/** Default empty dependency version. **/
static public var DEFAULT(default, null) = new DependencyVersion('');
Expand Down Expand Up @@ -230,6 +197,8 @@ typedef Infos = {
var Bsd = 'BSD';
var Public = 'Public';
var Apache = 'Apache';
@:disallowed
var Unknown = 'Unknown';
}

/** Class providing functions for working with project information. **/
Expand Down Expand Up @@ -264,28 +233,6 @@ class Data {
return safe(lib)+"-"+safe(ver)+".zip";
}

/**
Returns the latest version from `info`, or `null` if no releases are found.

By default preview versions are ignored. If `preview` is passed in,
preview versions will be checked first and will only be skipped
if calling `preview` with the `Preview` type of the version,
and it is only skipped if the call returns `false`.
**/
static public function getLatest(info:ProjectInfos, ?preview:SemVer.Preview->Bool):Null<SemVer> {
if (info.versions.length == 0) return null;
if (preview == null)
preview = function (p) return p == null;

var versions = info.versions.copy();
versions.sort(function (a, b) return -SemVer.compare(a.name, b.name));

for (v in versions)
if (preview(v.name.preview)) return v.name;

return versions[0].name;
}

/**
Returns the directory that contains *haxelib.json*.
If it is at the root, `""`.
Expand Down Expand Up @@ -333,7 +280,7 @@ class Data {
}

/** Retrieves the haxelib.json data from `zip`, validating it according to `check`. **/
public static function readInfos( zip : List<Entry>, check : CheckLevel ) : Infos
public static function readDataFromZip( zip : List<Entry>, check : CheckLevel ) : Infos
return readData(Reader.unzip(getJson(zip)).toString(), check);

/** Throws an exception if the classpath in `infos` does not exist in `zip`. **/
Expand All @@ -350,6 +297,28 @@ class Data {
}
}

static function cleanDependencies(dependencies:Null<Dependencies>):Void {
if (dependencies == null)
return;
#if haxe4
for (name => version in dependencies) {
if (!DependencyVersion.isUsable(version))
Reflect.setField(dependencies, name, DependencyVersion.DEFAULT); // TODO: this is pure evil
}
#else
for (name in dependencies.getNames()) {
if (!DependencyVersion.isUsable(Reflect.field(dependencies, name)))
Reflect.setField(dependencies, name, DependencyVersion.DEFAULT); // TODO: this is pure evil
}
#end
}

static inline function isStringArray(array:Array<String>) {
return #if (haxe_ver < 4.1) Std.is(array, Array) && array.foreach(function(item) { return Std.is(item, String);});
#else (array is Array) && array.foreach((item) -> item is String);
#end
}

/**
Extracts project information from `jsondata`, validating it according to `check`.

Expand All @@ -369,41 +338,40 @@ class Data {
url : '',
version : SemVer.DEFAULT,
releasenote: 'No haxelib.json found',
license: Mit,
license: Unknown,
description: 'No haxelib.json found',
contributors: [],
}

if (check >= CheckLevel.CheckData)
if (check >= CheckLevel.CheckData) {
Validator.validate(doc);
else {
} else {
if (doc.name.validate() != None)
doc.name = defaultName;
if (!doc.version.valid)
doc.version = SemVer.DEFAULT;
if (doc.license == null || doc.license == '')
doc.license = Unknown;
if (!isStringArray(doc.contributors))
doc.contributors = [];
if (doc.releasenote == null)
doc.releasenote = '';
cleanDependencies(doc.dependencies);
}

//TODO: we have really weird ways to go about nullability and defaults

// these may have been ommitted even in a valid file
if (doc.dependencies == null)
doc.dependencies = {};

for (dep in doc.dependencies)
if (!DependencyVersion.isValid(dep.version))
Reflect.setField(doc.dependencies, dep.name, DependencyVersion.DEFAULT);//TODO: this is pure evil

if (doc.classPath == null)
doc.classPath = '';

if (doc.name.validate() != None)
doc.name = defaultName;

if (doc.description == null)
doc.description = '';

if (doc.tags == null)
doc.tags = [];

if (doc.url == null)
doc.url = '';
if (!isStringArray(doc.tags))
doc.tags = [];

return doc;
}
Expand Down
78 changes: 78 additions & 0 deletions src/haxelib/MetaData.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package haxelib;
/**
Contains definitions of meta data about libraries and their versions used in the Haxelib database
**/

/** Information on a `user` in the Haxelib database. **/
typedef UserInfos = {
/** The user's name. **/
var name : String;
/** The user's full name. **/
var fullname : String;
/** The user's email address. **/
var email : String;
/** An array of projects for which the user is a contributor. **/
var projects : Array<String>;
}

/** Information on a specific version of a project in the Haxelib database. **/
typedef VersionInfos = {
/** The release date of the version. **/
var date : String;
/** The version "name" in SemVer form. **/
var name : SemVer;//TODO: this should eventually be called `number`
/** The number of downloads this version of the library has had. **/
var downloads : Int;
/** The release note that came with this release. **/
var comments : String;
}

/** Information on a project in the Haxelib database. **/
typedef ProjectInfos = {
/** The project name. **/
var name : String;
/** The project's description. **/
var desc : String;
/** A link to the project's website. **/
var website : String;
/** The username of the owner of the project. **/
var owner : String;
/** An array of contributor's user names and full names. **/
var contributors : Array<{ name:String, fullname:String }>;
/** The license under which the project is released. **/
var license : String;
/** The current version of the project. **/
var curversion : String;
/** The total number of downloads the project has. **/
var downloads : Int;
/** An array of `VersionInfos` for each release of the library. **/
var versions : Array<VersionInfos>;
/** The project's tags. **/
var tags : List<String>;
}

class MetaData {
/**
Returns the latest version from `info`, or `null` if no releases are found.

By default preview versions are ignored. If `preview` is passed in,
preview versions will be checked first and will only be skipped
if calling `preview` with the `Preview` type of the version,
and it is only skipped if the call returns `false`.
**/
public static function getLatest(info:ProjectInfos, ?preview:SemVer.Preview->Bool):Null<SemVer> {
if (info.versions.length == 0)
return null;
if (preview == null)
preview = function(p) return p == null;

var versions = info.versions.copy();
versions.sort(function(a, b) return -SemVer.compare(a.name, b.name));

for (v in versions)
if (preview(v.name.preview))
return v.name;

return versions[0].name;
}
}
2 changes: 1 addition & 1 deletion src/haxelib/SiteApi.hx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*/
package haxelib;

import haxelib.Data;
import haxelib.MetaData;
import haxe.ds.*;

interface SiteApi {
Expand Down
18 changes: 9 additions & 9 deletions src/haxelib/Util.hx
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,30 @@ class Util {

macro static public function getHaxelibVersionLong() {
var version:String = readVersionFromHaxelibJson();
// check if the .git folder exist
// prevent getting the git info of a parent directory
if (!sys.FileSystem.isDirectory(".git"))
return macro $v{version};

var p;
try {
//check if the .git folder exist
//prevent getting the git info of a parent directory
if (!sys.FileSystem.isDirectory(".git"))
throw "Not a git repo.";

//get commit sha
p = new sys.io.Process("git", ["rev-parse", "HEAD"]);
final sha = p.stdout.readAll().toString().trim();
var sha = p.stdout.readAll().toString().trim();
Simn marked this conversation as resolved.
Show resolved Hide resolved
p.close();

//check to see if there is changes, staged or not
p = new sys.io.Process("git", ["status", "--porcelain"]);
final changes = p.stdout.readAll().toString().trim();
var changes = p.stdout.readAll().toString().trim();
p.close();

version += switch(changes) {
var longVersion = version + switch(changes) {
case "":
' ($sha)';
case _:
' ($sha - dirty)';
}
return macro $v{version};
return macro $v{longVersion};
} catch(e:Dynamic) {
if (p != null) p.close();
return macro $v{version};
Expand Down
2 changes: 1 addition & 1 deletion src/haxelib/Validator.hx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class Validator {
var name = a.module + '.' + a.name;
var options:Array<Expr> = [
for (f in a.impl.get().statics.get())
if (f.kind.match(FVar(_, _)))
if (f.kind.match(FVar(_, _)) && !f.meta.has(':disallowed'))
macro @:pos(pos) $p{(name+'.'+f.name).split('.')}
];

Expand Down
Loading