diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..31b347b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,7 @@
+
+Changelog
+=========
+
+1.0.0 (2016-04-21)
+
+* Initial stable release.
diff --git a/README.md b/README.md
index 97ca96a..d6ea7d5 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,24 @@
# FOMOD Validator
+[](https://ci.appveyor.com/project/GandaG/fomod-validator)
*Validate your FOMOD installers.*
## Overview
-*TODO*
+This little app allows you validate and check for common errors in your FOMOD installers. Simply place the path to your package (the source files) and press `Ok` and, according to your selections, it will provide with your results. Simple, easy and effective.
+
+## Download
+
+Get either the [latest stable release](https://github.com/GandaG/fomod-validator/releases/latest) or, for the more daring, the [bleeding edge release](https://ci.appveyor.com/project/GandaG/fomod-validator/build/artifacts).
## Installation
-* Download the zip file corresponding to your OS from the [latest release](https://github.com/GandaG/fomod-validator/releases/latest);
* Extract the folder within to a location of your choice;
* Run the "FOMOD Validator" executable.
## Contributing
-This repo uses a ***.settings*** file to define all the necessary settings. This file follows this syntax:
+This repo uses a `.settings` file to define all the necessary settings. This file follows this syntax:
```
[git]
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..55d5ceb
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,21 @@
+install:
+ - .\dev\appveyor-bootstrap.bat
+
+build: off
+
+test_script:
+ - inv build
+
+artifacts:
+ - path: dist\*
+ name: windows_build
+
+deploy:
+ - provider: GitHub
+ auth_token:
+ secure: iMaZrvVT+OI/9jRs8LyOvmzVqIBa0/jpiK96wNzZww/KqKsMcferhIeSK7faNzOo
+ artifact: windows_build
+ description: '[Changelog.](https://github.com/GandaG/fomod-validator/blob/master/CHANGELOG.md)'
+ force_update: true
+ on:
+ appveyor_repo_tag: true
diff --git a/dev/appveyor-bootstrap.bat b/dev/appveyor-bootstrap.bat
new file mode 100644
index 0000000..3023550
--- /dev/null
+++ b/dev/appveyor-bootstrap.bat
@@ -0,0 +1,13 @@
+@echo off
+
+set PATH=C:\Miniconda-x64;C:\Miniconda-x64\Scripts;%PATH%
+
+conda create -y -n fomod-validator^
+ -c https://conda.anaconda.org/mmcauliffe^
+ -c https://conda.anaconda.org/anaconda^
+ pyqt5=5.5.1 python=3.5.1 lxml=3.5.0
+call activate fomod-validator
+
+pip install pip -U
+pip install setuptools -U --ignore-installed
+pip install -r dev\reqs.txt
diff --git a/dev/pyinstaller-build.spec b/dev/pyinstaller-build.spec
index 9ac3e2d..62d73a9 100644
--- a/dev/pyinstaller-build.spec
+++ b/dev/pyinstaller-build.spec
@@ -6,7 +6,8 @@ import os
a = Analysis(['pyinstaller-bootstrap.py'],
pathex=[os.getcwd()],
binaries=None,
- datas=[('../setup.cfg', '.'),],
+ datas=[('../setup.cfg', '.'),
+ ('../resources', 'resources/'),],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
diff --git a/dev/reqs.txt b/dev/reqs.txt
index 7f25038..9559420 100644
--- a/dev/reqs.txt
+++ b/dev/reqs.txt
@@ -1,4 +1,8 @@
bumpversion==0.5.3
invoke==0.12.2
lxml==3.5.0
+pkginfo==1.2.1
PyInstaller==3.1.1
+requests==2.9.1
+requests-toolbelt==0.6.0
+twine==1.6.5
diff --git a/fomod/__main__.py b/fomod/__main__.py
index 8d4ae69..d0f8f37 100644
--- a/fomod/__main__.py
+++ b/fomod/__main__.py
@@ -14,10 +14,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import sys
+from PyQt5.QtWidgets import QApplication
+from . import exceptions, mainframe
+
def main():
- pass
+ sys.excepthook = exceptions.excepthook
+ app = QApplication(sys.argv)
+ win = mainframe.Mainframe()
+ sys.exit(app.exec_())
if __name__ == "__main__":
main()
diff --git a/fomod/exceptions.py b/fomod/exceptions.py
new file mode 100644
index 0000000..9a4ce5a
--- /dev/null
+++ b/fomod/exceptions.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+
+# Copyright 2016 Daniel Nunes
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import traceback
+import io
+from PyQt5 import QtWidgets, QtGui
+from . import __version__
+
+
+def excepthook(exc_type, exc_value, tracebackobj):
+ """
+ Global function to catch unhandled exceptions.
+
+ @param exc_type exception type
+ @param exc_value exception value
+ @param tracebackobj traceback object
+ """
+ notice = (
+ "An unhandled exception occurred. Please report the problem"
+ " at Github,"
+ " or STEP.")
+ version_info = __version__
+
+ tbinfofile = io.StringIO()
+ traceback.print_tb(tracebackobj, None, tbinfofile)
+ tbinfofile.seek(0)
+ tbinfo = tbinfofile.read()
+ errmsg = 'Error information:\n\nVersion: {}\n{}: {}\n'.format(version_info, str(exc_type), str(exc_value))
+ sections = [errmsg, tbinfo]
+ msg = '\n'.join(sections)
+
+ errorbox = QtWidgets.QMessageBox()
+ errorbox.setText(notice)
+ errorbox.setDetailedText(msg)
+ errorbox.setWindowTitle("Nobody Panic!")
+ errorbox.exec_()
diff --git a/fomod/mainframe.py b/fomod/mainframe.py
new file mode 100644
index 0000000..f2df075
--- /dev/null
+++ b/fomod/mainframe.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+
+# Copyright 2016 Daniel Nunes
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from PyQt5 import uic, QtWidgets, QtCore
+from os.path import join, expanduser
+from . import cur_folder
+
+base_ui = uic.loadUiType(join(cur_folder, "resources", "mainframe.ui"))
+
+
+class Mainframe(base_ui[0], base_ui[1]):
+ def __init__(self):
+ super(Mainframe, self).__init__()
+ self.setupUi(self)
+
+ self.setWindowFlags(QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
+
+ self.buttonBox.accepted.connect(self.accepted)
+ self.buttonBox.rejected.connect(self.rejected)
+ self.path_button.clicked.connect(self.path_button_clicked)
+
+ self.package_path = ""
+ self.checked_validate = False
+ self.checked_warnings = False
+
+ self.show()
+
+ def accepted(self):
+ from .validator import validate, check_warnings, \
+ ValidationError, WarningError, MissingFolderError, MissingFileError
+
+ self.package_path = self.path_text.text()
+ self.checked_validate = self.check_validate.isChecked()
+ self.checked_warnings = self.check_warnings.isChecked()
+
+ self.close()
+
+ try:
+ errorbox = QtWidgets.QMessageBox()
+
+ if self.checked_validate:
+ validate(self.package_path, cur_folder)
+
+ if self.checked_warnings:
+ log = check_warnings(self.package_path)
+
+ errorbox.setText("All good!")
+ errorbox.setWindowTitle("Yay!")
+ errorbox.exec_()
+ return
+ except ValidationError as v:
+ errorbox.setText(str(v))
+ errorbox.setWindowTitle("Invalid File(s)")
+ errorbox.exec_()
+ return
+ except WarningError as w:
+ errorbox.setText(str(w))
+ errorbox.setWindowTitle("Warnings Log")
+ errorbox.exec_()
+ return
+ except (MissingFileError, MissingFolderError) as m:
+ errorbox.setText(str(m))
+ errorbox.setWindowTitle("I/O Error")
+ errorbox.exec_()
+ return
+
+ def rejected(self):
+ self.close()
+
+ def path_button_clicked(self):
+ open_dialog = QtWidgets.QFileDialog()
+ self.path_text.setText(open_dialog.getExistingDirectory(self, "Package directory:", expanduser("~")))
diff --git a/fomod/validator/__init__.py b/fomod/validator/__init__.py
new file mode 100644
index 0000000..3beb94c
--- /dev/null
+++ b/fomod/validator/__init__.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+# Copyright 2016 Daniel Nunes
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from .validate import validate
+from .warnings import check_warnings
+from .exceptions import ValidationError, WarningError, MissingFolderError, MissingFileError
diff --git a/fomod/validator/exceptions.py b/fomod/validator/exceptions.py
new file mode 100644
index 0000000..a3880dc
--- /dev/null
+++ b/fomod/validator/exceptions.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+# Copyright 2016 Daniel Nunes
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class MissingFolderError(Exception):
+ def __init__(self, folder):
+ self.msg = folder + " folder is missing."
+ Exception.__init__(self, self.msg)
+
+
+class MissingFileError(Exception):
+ def __init__(self, file):
+ self.msg = file + " file is missing."
+ Exception.__init__(self, self.msg)
+
+
+class ValidationError(Exception):
+ def __init__(self, msg=""):
+ self.msg = msg
+ Exception.__init__(self, self.msg)
+
+
+class WarningError(Exception):
+ def __init__(self, msg=""):
+ self.msg = msg
+ Exception.__init__(self, self.msg)
diff --git a/fomod/validator/utility.py b/fomod/validator/utility.py
new file mode 100644
index 0000000..59b64c5
--- /dev/null
+++ b/fomod/validator/utility.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+# Copyright 2016 Daniel Nunes
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from os import listdir
+from .exceptions import MissingFileError, MissingFolderError
+
+
+def check_fomod(package_path):
+ existing_fomod = False
+ fomod_folder = "fomod"
+
+ try:
+ for folder in listdir(package_path):
+ if folder.upper() == "FOMOD":
+ existing_fomod = True
+ fomod_folder = folder
+ except FileNotFoundError:
+ raise MissingFolderError(fomod_folder)
+
+ if not existing_fomod:
+ raise MissingFolderError(fomod_folder)
+
+ return fomod_folder
+
+
+def check_file(fomod_path):
+ config_exists = False
+ config_file = "moduleconfig.xml"
+
+ for file in listdir(fomod_path):
+ if file.upper() == "MODULECONFIG.XML":
+ config_exists = True
+ config_file = file
+
+ if not config_exists:
+ raise MissingFileError(config_file)
+
+ return config_file
diff --git a/fomod/validator/validate.py b/fomod/validator/validate.py
new file mode 100644
index 0000000..074cd17
--- /dev/null
+++ b/fomod/validator/validate.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+# Copyright 2016 Daniel Nunes
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from os.path import join
+from lxml import etree
+from .utility import check_fomod, check_file
+from .exceptions import MissingFileError, MissingFolderError, ValidationError
+
+
+def validate(package_path, cur_folder):
+ try:
+ fomod_folder = check_fomod(package_path)
+ config_file = check_file(join(package_path, fomod_folder))
+ xmlschema_doc = etree.parse(join(cur_folder, "resources", "mod_schema.xsd"))
+ xmlschema = etree.XMLSchema(xmlschema_doc)
+ xmlschema.assertValid(etree.parse(join(package_path, fomod_folder, config_file)))
+ except (MissingFolderError, MissingFileError):
+ raise
+ except etree.DocumentInvalid as e:
+ raise ValidationError(check_file(join(package_path, check_fomod(package_path))) +
+ " is invalid with error message:\n\n" + str(e))
diff --git a/fomod/validator/warnings.py b/fomod/validator/warnings.py
new file mode 100644
index 0000000..f02aec0
--- /dev/null
+++ b/fomod/validator/warnings.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+# Copyright 2016 Daniel Nunes
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from os.path import join, isfile, isdir
+from lxml import etree
+from .utility import check_file, check_fomod
+from .exceptions import MissingFileError, MissingFolderError, WarningError
+
+
+def check_warnings(package_path):
+ class ElementLog(object):
+ def __init__(self, elements, title, msg):
+ self.elements = {}
+ for elem_ in elements:
+ if elem_.tag not in self.elements.keys():
+ self.elements[elem_.tag] = [elem_]
+ else:
+ self.elements[elem_.tag].append(elem_)
+
+ self.title = title
+
+ self.msgs = {}
+ for elem_ in elements:
+ self.msgs[elem_.tag] = msg.replace("{}", elem_.tag)
+
+ result = "Warnings Log
"
+ result_initial = result
+
+ repeatable_tags = ("moduleName", "moduleImage", "moduleDependencies",
+ "requiredInstallFiles", "installSteps", "conditionalFileInstalls", "")
+ repeated_elems = []
+ repeated_elems_msg = "The tag {} has several occurrences, this may produce unexpected results."
+ folder_tags = ("folder",)
+ missing_folders = []
+ missing_folders_msg = "These source folder(s) weren't found inside the package. " \
+ "The installers ignore this so be sure to fix it."
+ file_tags = ("file",)
+ missing_files = []
+ missing_files_msg = "These source file(s) weren't found inside the package. " \
+ "The installers ignore this so be sure to fix it."
+
+ try:
+ fomod_folder = check_fomod(package_path)
+ config_file = check_file(join(package_path, fomod_folder))
+ config_tree = etree.parse(join(package_path, fomod_folder, config_file))
+
+ for element in config_tree.getroot().iter():
+ if element.tag in repeatable_tags:
+ list_ = repeated_elems
+ elif element.tag in folder_tags:
+ list_ = missing_folders
+ elif element.tag in file_tags:
+ list_ = missing_files
+ else:
+ continue
+
+ list_.append(element)
+
+ result_repeat = []
+ result_folder = []
+ result_file = []
+
+ for elem in repeated_elems:
+ if sum(1 for value in repeated_elems if value.tag == elem.tag) >= 2:
+ result_repeat.append(elem)
+
+ for elem in missing_folders:
+ if not isdir(join(package_path, elem.get("source"))):
+ result_folder.append(elem)
+
+ for elem in missing_files:
+ if not isfile(join(package_path, elem.get("source"))):
+ result_file.append(elem)
+
+ repeat_log = None
+ folder_log = None
+ file_log = None
+
+ if result_repeat:
+ repeat_log = ElementLog(result_repeat, "Repeated Elements", repeated_elems_msg)
+ if result_folder:
+ folder_log = ElementLog(result_folder, "Missing Source Folders", missing_folders_msg)
+ if result_file:
+ file_log = ElementLog(result_file, "Missing Source Files", missing_files_msg)
+
+ result += _log_warnings([repeat_log, folder_log, file_log])
+
+ if result != result_initial:
+ raise WarningError(result)
+ except (MissingFolderError, MissingFileError):
+ raise
+
+
+def _log_warnings(list_):
+ result = ""
+
+ for log in list_:
+ if log:
+ result += "" + log.title + "
"
+
+ for tag in log.elements:
+ result += "Lines"
+ for elem in log.elements[tag]:
+ result += " " + str(elem.sourceline) + ","
+ result = result[:-1]
+ result += ": " + log.msgs[tag] + "
"
+
+ result += "
"
+
+ return result
diff --git a/resources/mainframe.ui b/resources/mainframe.ui
new file mode 100644
index 0000000..9c53cb7
--- /dev/null
+++ b/resources/mainframe.ui
@@ -0,0 +1,147 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 566
+ 151
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 436
+ 151
+
+
+
+
+ 566
+ 151
+
+
+
+ FOMOD Validator
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+ Path to package...
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ ...
+
+
+ false
+
+
+ false
+
+
+
+
+
+ -
+
+
+ Validate FOMOD files.
+
+
+ true
+
+
+
+ -
+
+
+ Include warnings (non-existing files, invalid paths, tag recommendations).
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+ true
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/mod_schema.xsd b/resources/mod_schema.xsd
new file mode 100644
index 0000000..9c546aa
--- /dev/null
+++ b/resources/mod_schema.xsd
@@ -0,0 +1,687 @@
+
+
+
+
+ The main element containing the module configuration info.
+
+
+
+
+
+ Describes the configuration of a module.
+
+
+
+
+ The name of the module.
+
+
+
+
+ The module logo. [Ignored in Mod Organizer]
+
+
+
+
+ Items upon which the module depends.
+
+
+
+
+ The list of files and folders that must be installed for this module.
+
+
+
+
+ The list of install steps that determine which files (or plugins) that may optionally be installed for this module.
+
+
+
+
+ The list of optional files that may optionally be installed for this module, base on condition flags.
+
+
+
+
+
+
+
+ Describes the display properties of the module title.
+
+
+
+
+
+ The identifying name of the condition flag. [Ignored in Mod Organizer]
+
+
+
+ The possible title positions.
+
+
+
+
+ Positions the title on the left side of the form header.
+
+
+
+
+ Positions the title on the right side of the form header.
+
+
+
+
+ Positions the title on the right side of the image in the form header.
+
+
+
+
+
+
+
+ The colour to use for the title. [Ignored in Mod Organizer]
+
+
+
+
+
+
+
+
+ An image.
+
+
+
+ The path to the image in the FOMod. If omitted the FOMod's screenshot is used.
+
+
+
+
+ Whether or not the image should be displayed.
+
+
+
+
+ Whether or not the fade effect should be displayed. This value is ignored if showImage is false.
+
+
+
+
+ The height to use for the image. Note that there is a minimum height that is enforced based on the user's settings.
+
+
+
+
+
+
+ A dependency that is made up of one or more dependencies.
+
+
+
+
+ The relation of the contained dependencies.
+
+
+
+
+
+ Indicates all contained dependencies must be satisfied in order for this dependency to be satisfied.
+
+
+
+
+ Indicates at least one listed dependency must be satisfied in order for this dependency to be satisfied.
+
+
+
+
+
+
+
+
+
+ The group of possible dependencies.
+
+
+
+
+
+ Specifies that a mod must be in a specified state.
+
+
+
+
+ Specifies that a condition flag must have a specific value.
+
+
+
+
+ Specifies a minimum required version of the installed game. [Ignored in Mod Organizer]
+
+
+
+
+ A list of mods and their states against which to match the user's installation.
+
+
+
+
+
+
+
+
+ A mod upon which the type of a plugin depends.
+
+
+
+ The file of the mod upon which a the plugin depends.
+
+
+
+
+ The state of the mod file.
+
+
+
+
+
+ Indicates the mod file is not installed.
+
+
+
+
+ Indicates the mod file is installed, but not active.
+
+
+
+
+ Indicates the mod file is installed and active.
+
+
+
+
+
+
+
+
+
+ A condition flag upon which the type of a plugin depends.
+
+
+
+ The name of the condition flag upon which a the plugin depends.
+
+
+
+
+ The value of the condition flag upon which a the plugin depends.
+
+
+
+
+
+
+ A required minimum version of an item.
+
+
+
+ The required minimum version of the item.
+
+
+
+
+
+
+ A list of files and folders.
+
+
+
+
+
+ A file belonging to the plugin or module.
+
+
+
+
+ A folder belonging to the plugin or module.
+
+
+
+
+
+
+
+
+ A file or folder that may be installed as part of a module or plugin.
+
+
+
+ The path to the file or folder in the FOMod.
+
+
+
+
+ The path to which the file or folder should be installed. If omitted, the destination is the same as the source.
+
+
+
+
+ Indicates that the file or folder should always be installed, regardless of whether or not the plugin has been selected. [Ignored in Mod Organizer]
+
+
+
+
+ Indicates that the file or folder should always be installed if the plugin is not NotUsable, regardless of whether or not the plugin has been selected. [Ignored in Mod Organizer]
+
+
+
+
+ A number describing the relative priority of the file or folder. A higher number indicates the file or folder should be installed after the items with lower numbers. This value does not have to be unique.
+
+
+
+
+
+
+ A list of install steps.
+
+
+
+
+ A list of install steps for the mod.
+
+
+
+
+
+ The order by which to list the steps.
+
+
+
+
+
+
+ A step in the install process containing groups of optional plugins.
+
+
+
+
+ The pattern against which to match the conditional flags and installed files. If the pattern is matched, then the install step will be visible.
+
+
+
+
+ The list of optional files (or plugins) that may optionally be installed for this module.
+
+
+
+
+
+ The name of the install step.
+
+
+
+
+
+
+ A list of plugin groups.
+
+
+
+
+ A group of plugins for the mod.
+
+
+
+
+
+ The order by which to list the groups.
+
+
+
+
+
+
+ A group of plugins.
+
+
+
+
+ The list of plugins in the group.
+
+
+
+
+
+ The name of the group.
+
+
+
+
+ The type of the group.
+
+
+
+
+
+ At least one plugin in the group must be selected.
+
+
+
+
+ At most one plugin in the group must be selected.
+
+
+
+
+ Exactly one plugin in the group must be selected.
+
+
+
+
+ All plugins in the group must be selected.
+
+
+
+
+ Any number of plugins in the group may be selected.
+
+
+
+
+
+
+
+
+
+ A list of plugins.
+
+
+
+
+ A mod plugin belonging to a group.
+
+
+
+
+
+ The order by which to list the plugins.
+
+
+
+
+
+
+ A plugin.
+
+
+
+
+ A description of the plugin.
+
+
+
+
+ The optional image associated with a plugin.
+
+
+
+
+
+
+ The list of files and folders that need to be installed for the plugin.
+
+
+
+
+ The list of condition flags to set if the plugin is in the appropriate state.
+
+
+
+
+
+
+ The list of condition flags to set if the plugin is in the appropriate state.
+
+
+
+
+ The list of files and folders that need to be installed for the plugin.
+
+
+
+
+
+
+ Describes the type of the plugin.
+
+
+
+
+
+ The name of the plugin.
+
+
+
+
+
+
+ An image.
+
+
+
+ The path to the image in the FOMod.
+
+
+
+
+
+
+ A list of condition flags to set if a plugin is in the appropriate state.
+
+
+
+
+ A condition flag to set if the plugin is selected.
+
+
+
+
+
+
+
+ A condition flag to set if a plugin is selected.
+
+
+
+
+
+ The identifying name of the condition flag.
+
+
+
+
+
+
+
+
+ Describes the type of a plugin.
+
+
+
+
+ Used when the plugin type is dependent upon the state of other mods.
+
+
+
+
+ The type of the plugin.
+
+
+
+
+
+
+
+ A plugin type that is dependent upon the state of other mods.
+
+
+
+
+ The default type of the plugin used if none of the specified dependency states are satisfied.
+
+
+
+
+ The list of dependency patterns against which to match the user's installation. The first pattern that matches the user's installation determines the type of the plugin.
+
+
+
+
+
+
+
+ The type of a given plugin.
+
+
+
+ The name of the plugin type.
+
+
+
+
+
+
+ The possible plugin types.
+
+
+
+
+ Indicates the plugin must be installed.
+
+
+
+
+ Indicates the plugin is optional.
+
+
+
+
+ Indicates the plugin is recommended for stability.
+
+
+
+
+ Indicates that using the plugin could result in instability (i.e., a prerequisite plugin is missing).
+
+
+
+
+
+ Indicates that using the plugin could result in instability if loaded
+ with the currently active plugins (i.e., a prerequisite plugin is missing),
+ but that the prerequisite plugin is installed, just not activated.
+
+
+
+
+
+
+
+
+ A list of dependency patterns.
+
+
+
+
+ A specific pattern of mod files and condition flags against which to match the user's installation.
+
+
+
+
+
+
+
+ A pattern of mod files and condition flags that determine the type of a plugin.
+
+
+
+
+ The list of mods and their states against which to match the user's installation.
+
+
+
+
+ The type of the plugin.
+
+
+
+
+
+
+
+ The possible orders of items.
+
+
+
+
+ Indicates the items are to be ordered ascending alphabetically.
+
+
+
+
+ Indicates the items are to be ordered descending alphabetically.
+
+
+
+
+ Indicates the items are to be ordered as listed in the configuration file.
+
+
+
+
+
+
+
+ A list of optional files that may optionally be installed for this module, base on condition flags.
+
+
+
+
+ The list of patterns against which to match the conditional flags and installed files. All matching patterns will have their files installed.
+
+
+
+
+
+
+
+ A list of conditional install patterns.
+
+
+
+
+ A specific pattern of mod files and condition flags against which to match the user's installation.
+
+
+
+
+
+
+
+ A pattern of mod files and conditional flags that determine whether to install specific files.
+
+
+
+
+ The list of mods and their states against which to match the user's installation.
+
+
+
+
+ The files and filders to install if the pattern is matched.
+
+
+
+
+
diff --git a/setup.cfg b/setup.cfg
index 94e2312..2d333e8 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,7 +1,9 @@
[bumpversion]
-current_version = 0.0.0
+current_version = 1.0.0
current_build = 0
+[bumpversion:file:setup.py]
+
[bdist_wheel]
-universal = 1
+universal = 0
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..7aecba7
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+setup(
+ name='fomod-validator',
+ version='1.0.0',
+ packages=['validator'],
+ package_dir={'validator': 'fomod/validator'},
+ url='https://github.com/GandaG/fomod-validator',
+ license='Apache 2.0',
+ author='Daniel Nunes',
+ author_email='gandaganza@gmail.com',
+ description='Validate your FOMOD installers.',
+ install_requires=['lxml'],
+)
diff --git a/tasks.py b/tasks.py
index 3f864ed..2fa12d9 100644
--- a/tasks.py
+++ b/tasks.py
@@ -58,7 +58,7 @@ def build():
from fnmatch import fnmatch
# set which files will be included within the archive.
- included_files = ["LICENSE", "README.md"]
+ included_files = ["LICENSE", "README.md", "CHANGELOG.md"]
archive_name = "validator" # the archive's name
try: