Skip to content

Commit

Permalink
Merge pull request #218 from pjf/129_filter
Browse files Browse the repository at this point in the history
Added filter support. Deprecated many things.
  • Loading branch information
AlexanderDzhoganov committed Oct 31, 2014
2 parents 72bdd02 + a81fc52 commit 5ecf08a
Show file tree
Hide file tree
Showing 25 changed files with 385 additions and 208 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ script:
- cd CKAN
- xbuild CKAN.sln
- nunit-console --exclude=FlakyNetwork Tests/bin/Debug/Tests.dll
- ../bin/validate-netkan

before_deploy:
- cd ..
Expand Down
53 changes: 19 additions & 34 deletions CKAN.schema
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@
"description" : "Where file should be installed to",
"$ref" : "#/definitions/install_to"
},
"filter" : {
"description" : "List of files and directories that should be filtered from the install",
"$ref" : "#/definitions/one_or_more_strings"
},
"filter_regexp" : {
"description" : "List of regexps that should filter files from this install",
"$ref" : "#/definitions/one_or_more_strings"
},
"requires" : {
"description" : "A required mod for this install part",
"$ref" : "#/definitions/identifier"
Expand All @@ -171,40 +179,6 @@
},
"required" : [ "file", "install_to" ]
}
},
"bundles" : {
"description" : "Modules bundled with this mod",
"type" : "array",
"items" : {
"type" : "object",
"properties" : {
"file" : {
"description" : "Path to the bundled module",
"type" : "string"
},
"identifier" : {
"description" : "Identifier of the bundled mod",
"$ref" : "#/definitions/identifier"
},
"version" : {
"description" : "Version of bundled module",
"$ref" : "#/definitions/version"
},
"install_to" : {
"description" : "Where bundle should be installed to",
"$ref" : "#/definitions/install_to"
},
"license" : {
"description" : "The license(s) the bundled mod is used under",
"$ref" : "#/definitions/licenses"
},
"required" : {
"description" : "Is this module required for installation?",
"type" : "boolean"
}
},
"required" : [ "file", "identifier", "version", "install_to", "required", "license" ]
}
}
},
"required" : [
Expand Down Expand Up @@ -303,6 +277,17 @@
"uniqueItems" : true
}
]
},
"one_or_more_strings" : {
"description" : "One or more strings",
"oneOf" : [
{ "type" : "string" },
{
"type" : "array",
"items" : { "type" : "string" },
"uniqueItems" : true
}
]
}
}
}
1 change: 1 addition & 0 deletions CKAN/CKAN/CKAN.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
<Compile Include="Registry\InstalledModule.cs" />
<Compile Include="Registry\Registry.cs" />
<Compile Include="Registry\RegistryManager.cs" />
<Compile Include="Types\ModuleInstallDescriptor.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
Expand Down
35 changes: 1 addition & 34 deletions CKAN/CKAN/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,6 @@ public class RelationshipDescriptor
public string version;
}

public abstract class InstallableDescriptor
{
public /* required */ string file;
public /* required */ string install_to;
}

public class BundledModuleDescriptor : InstallableDescriptor
{
public /* required */ string identifier;
public /* required */ string license;
public /* required */ bool required;
public /* required */ string version;
}

public class GitHubResourceDescriptor
{
public bool releases;
Expand All @@ -51,14 +37,6 @@ public class ResourcesDescriptor
public KerbalStuffResourceDescriptor kerbalstuff;
}

public class ModuleInstallDescriptor : InstallableDescriptor
{
public string description;
public bool optional;
public bool overwrite;
public string requires;
}

public enum License
{
public_domain,
Expand Down Expand Up @@ -175,6 +153,7 @@ public class Module
[JsonProperty("name")] public string name;

[JsonProperty("pre_depends")] public RelationshipDescriptor[] pre_depends;

[JsonProperty("provides")] public string[] provides;

[JsonProperty("recommends")] public RelationshipDescriptor[] recommends;
Expand Down Expand Up @@ -265,17 +244,6 @@ public bool IsCompatibleKSP(KSPVersion version)
}
}

public class BundledModule : Module
{
public BundledModule(BundledModuleDescriptor stanza)
{
// For now, we just copy across the fields from our stanza.
version = new Version(stanza.version);
identifier = stanza.identifier;
license = stanza.license;
}
}

public class CkanInvalidMetadataJson : Exception
{
}
Expand All @@ -300,7 +268,6 @@ public class CkanModule : Module
// private static JsonSchema metadata_schema;
// private static string metadata_schema_path = "CKAN.schema";
// private static bool metadata_schema_missing_warning_fired;
[JsonProperty("bundles")] public BundledModuleDescriptor[] bundles;
[JsonProperty("install")] public ModuleInstallDescriptor[] install;
[JsonProperty("spec_version")] public string spec_version;

Expand Down
48 changes: 4 additions & 44 deletions CKAN/CKAN/ModuleInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,35 +393,6 @@ internal void Install(CkanModule module, string filename = null)
// Register our files.
registry.RegisterModule(new InstalledModule(module_files, module, DateTime.Now));

// Handle bundled mods, if we have them.
// TODO: Deprecate bundles!

if (module.bundles != null)
{
foreach (BundledModuleDescriptor stanza in module.bundles)
{
var bundled = new BundledModule(stanza);

Version ver = registry_manager.registry.InstalledVersion(bundled.identifier);

if (ver != null)
{
User.WriteLine(
"{0} {1} already installed, skipping bundled version {2}",
bundled.identifier, ver, bundled.version
);
continue;
}

// Not installed, so let's get about installing it!
var installed_files = new Dictionary<string, InstalledModuleFile>();

InstallComponent(stanza, filename, installed_files);

registry.RegisterModule(new InstalledModule(installed_files, bundled, DateTime.Now));
}
}

// Done! Save our registry changes!
// This is fine from a transaction standpoint, as we may not have an enclosing
// transaction, and if we do, they can always roll us back.
Expand Down Expand Up @@ -472,7 +443,6 @@ internal string Sha1Sum(string path)
internal static ModuleInstallDescriptor GenerateDefaultInstall(string identifier, ZipFile zipfile)
{
var stanza = new ModuleInstallDescriptor();
stanza.description = "Default install (autogenerated)";
stanza.install_to = "GameData";

// Candidate top-level directories.
Expand Down Expand Up @@ -518,7 +488,7 @@ internal static ModuleInstallDescriptor GenerateDefaultInstall(string identifier
/// Modifies the supplied module_files to contain the files installed.
/// This method should be avoided, as it may be removed in the future.
/// </summary>
internal void InstallComponent(InstallableDescriptor stanza, string zip_filename,
internal void InstallComponent(ModuleInstallDescriptor stanza, string zip_filename,
Dictionary<string, InstalledModuleFile> module_files)
{

Expand Down Expand Up @@ -578,7 +548,7 @@ internal void InstallModule(CkanModule module, string zip_filename, Dictionary<s
///
/// Throws a BadMetadataKraken if the stanza resulted in no files being returned.
/// </summary>
internal static List<InstallableFile> FindInstallableFiles(InstallableDescriptor stanza, ZipFile zipfile, KSP ksp)
internal static List<InstallableFile> FindInstallableFiles(ModuleInstallDescriptor stanza, ZipFile zipfile, KSP ksp)
{
string installDir;
bool makeDirs;
Expand Down Expand Up @@ -609,23 +579,13 @@ internal static List<InstallableFile> FindInstallableFiles(InstallableDescriptor
throw new BadInstallLocationKraken("Unknown install_to " + stanza.install_to);
}

// Is there a better way to extract a tree?
string filter = "^" + stanza.file + "(/|$)";

// O(N^2) solution, as we're walking the zipfile for each stanza.
// Surely there's a better way, although this is fast enough we may not care.

foreach (ZipEntry entry in zipfile)
{
// Skip things we don't want.
if (!Regex.IsMatch(entry.Name, filter))
{
continue;
}

// SKIP the file if it's a .CKAN file, these should never be copied to GameData.
if (Regex.IsMatch(entry.Name, ".CKAN", RegexOptions.IgnoreCase))
{
// Skips things not prescribed by our install stanza.
if (! stanza.IsWanted(entry.Name)) {
continue;
}

Expand Down
7 changes: 7 additions & 0 deletions CKAN/CKAN/Types/JsonSingleOrArrayConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
{
return token.ToObject<List<T>>();
}

// If the object is null, we'll return null. Otherwise end up with a list of null.
if (token.ToObject<T>() == null)
{
return null;
}

return new List<T> { token.ToObject<T>() };
}

Expand Down
82 changes: 82 additions & 0 deletions CKAN/CKAN/Types/ModuleInstallDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Newtonsoft.Json;

namespace CKAN
{
public class ModuleInstallDescriptor
{
public /* required */ string file;
public /* required */ string install_to;

[JsonConverter(typeof (JsonSingleOrArrayConverter<string>))]
public List<string> filter;

[JsonConverter(typeof (JsonSingleOrArrayConverter<string>))]
public List<string> filter_regexp;

[OnDeserialized]
internal void DeSerialisationFixes(StreamingContext like_i_could_care)
{
// Make sure our required fields exist.
if (file == null || install_to == null)
{
throw new BadMetadataKraken(null, "Install stanzas must have a file and install_to");
}
}

/// <summary>
/// Returns true if the path provided should be installed by this stanza.
/// </summary>
public bool IsWanted(string path)
{
// Make sure our path always uses slashes we expect.
string normalised_path = path.Replace('\\', '/');

// Make sure our internal state is consistent. Is there a better way of doing this?
filter = filter ?? new List<string> ();
filter_regexp = filter_regexp ?? new List<string> ();

// We want everthing that matches our 'file', either as an exact match,
// or as a path leading up to it.
string wanted_filter = "^" + Regex.Escape(this.file) + "(/|$)";

// If it doesn't match our install path, ignore it.
if (! Regex.IsMatch(normalised_path, wanted_filter))
{
return false;
}

// Skip the file if it's a ckan file, these should never be copied to GameData.
if (Regex.IsMatch(normalised_path, ".ckan$", RegexOptions.IgnoreCase))
{
return false;
}

// Get all our path segments. If our filter matches of any them, skip.
// All these comparisons are case insensitive.
var path_segments = new List<string>(normalised_path.ToLower().Split('/'));

foreach (string filter_text in this.filter)
{
if (path_segments.Contains(filter_text.ToLower()))
{
return false;
}
}

// Finally, check our filter regexpes.
foreach (string regexp in this.filter_regexp)
{
if (Regex.IsMatch(normalised_path, regexp))
{
return false;
}
}

// I guess we want this file after all. ;)
return true;
}
}
}
12 changes: 12 additions & 0 deletions CKAN/Tests/CKAN/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ public void MetaData()
Assert.AreEqual("https://github.com/KSP-KOS/KOS/issues", module.resources.bugtracker.ToString());
}

[Test]
public void FilterRead()
{
CkanModule module = CkanModule.FromJson(TestData.DogeCoinFlag_101());

// Assert known things about this mod.
Assert.IsNotNull(module.install[0].filter);
Assert.IsNotNull(module.install[0].filter_regexp);

Assert.AreEqual(2, module.install[0].filter.Count);
}

}
}

Loading

0 comments on commit 5ecf08a

Please sign in to comment.