public inbox for gentoo-portage-dev@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-portage-dev] [PATCH 0/4] GLEP 67 test cleanup & XML Schema for repoman
@ 2016-04-16 18:06 Michał Górny
  2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 1/4] tests: Stop using herds Michał Górny
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Michał Górny @ 2016-04-16 18:06 UTC (permalink / raw
  To: gentoo-portage-dev; +Cc: Michał Górny

Hi,

Just a quick batch of patches. The first two modify the tests removing
the use of obsolete <herd/> element and adding type="" to <maintainer/>
so that the tests will pass once updated schema is in place. The third
one replaces use of DTD with XML Schema in repoman so that more rules
of GLEP 68 can be enforced correctly. Finally, the fourth one includes
the current schema for tests and replaces the DTD with it.

It should be noted that repoman still needs a more complete GLEP 67
support and removal of obsolete herd code. I've limited my changes here
to the bare minimum needed to prevent repoman from accepting invalid
metadata.xml files. If time permits, I will be submitting further
improvements.


Michał Górny (4):
  tests: Stop using herds
  tests: Add type="" to <maintainer/>
  repoman: Use XML Schema for metadata.xml validation
  tests: Include metadata.xsd for repoman tests

 .travis.yml                                      |   4 +-
 MANIFEST.in                                      |   2 +-
 cnf/metadata.dtd                                 | 102 -----
 cnf/metadata.xsd                                 | 547 +++++++++++++++++++++++
 pym/portage/tests/emerge/test_simple.py          |   2 -
 pym/portage/tests/repoman/test_simple.py         |  11 +-
 pym/portage/tests/resolver/ResolverPlayground.py |  24 +-
 pym/repoman/_xml.py                              |  16 +-
 pym/repoman/checks/ebuilds/pkgmetadata.py        |   6 +-
 pym/repoman/metadata.py                          |  39 +-
 pym/repoman/scanner.py                           |   8 +-
 11 files changed, 590 insertions(+), 171 deletions(-)
 delete mode 100644 cnf/metadata.dtd
 create mode 100644 cnf/metadata.xsd

-- 
2.8.1



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [gentoo-portage-dev] [PATCH 1/4] tests: Stop using herds
  2016-04-16 18:06 [gentoo-portage-dev] [PATCH 0/4] GLEP 67 test cleanup & XML Schema for repoman Michał Górny
@ 2016-04-16 18:06 ` Michał Górny
  2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 2/4] tests: Add type="" to <maintainer/> Michał Górny
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Michał Górny @ 2016-04-16 18:06 UTC (permalink / raw
  To: gentoo-portage-dev; +Cc: Michał Górny

---
 pym/portage/tests/emerge/test_simple.py          |  2 --
 pym/portage/tests/repoman/test_simple.py         |  3 ---
 pym/portage/tests/resolver/ResolverPlayground.py | 22 ----------------------
 3 files changed, 27 deletions(-)

diff --git a/pym/portage/tests/emerge/test_simple.py b/pym/portage/tests/emerge/test_simple.py
index 394ed43..e5ecd4b 100644
--- a/pym/portage/tests/emerge/test_simple.py
+++ b/pym/portage/tests/emerge/test_simple.py
@@ -153,14 +153,12 @@ pkg_preinst() {
 			(
 				"dev-libs/A",
 				{
-					"herd" : "base-system",
 					"flags" : "<flag name='flag'>Description of how USE='flag' affects this package</flag>",
 				},
 			),
 			(
 				"dev-libs/B",
 				{
-					"herd" : "no-herd",
 					"flags" : "<flag name='flag'>Description of how USE='flag' affects this package</flag>",
 				},
 			),
diff --git a/pym/portage/tests/repoman/test_simple.py b/pym/portage/tests/repoman/test_simple.py
index 98220c4..720560b 100644
--- a/pym/portage/tests/repoman/test_simple.py
+++ b/pym/portage/tests/repoman/test_simple.py
@@ -133,21 +133,18 @@ class SimpleRepomanTestCase(TestCase):
 			(
 				"dev-libs/A",
 				{
-					"herd" : "base-system",
 					"flags" : "<flag name='flag' restrict='&gt;=dev-libs/A-0'>Description of how USE='flag' affects this package</flag>",
 				},
 			),
 			(
 				"dev-libs/B",
 				{
-					"herd" : "no-herd",
 					"flags" : "<flag name='flag'>Description of how USE='flag' affects this package</flag>",
 				},
 			),
 			(
 				"dev-libs/C",
 				{
-					"herd" : "no-herd",
 					"flags" : "<flag name='flag'>Description of how USE='flag' affects this package</flag>",
 				},
 			),
diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py
index 6bdf2c7..68e047a 100644
--- a/pym/portage/tests/resolver/ResolverPlayground.py
+++ b/pym/portage/tests/resolver/ResolverPlayground.py
@@ -47,7 +47,6 @@ class ResolverPlayground(object):
 	metadata_xml_template = """<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
 <pkgmetadata>
-<herd>%(herd)s</herd>
 <maintainer>
 <email>maintainer-needed@gentoo.org</email>
 <description>Description of the maintainership</description>
@@ -381,27 +380,6 @@ class ResolverPlayground(object):
 				#Create profile symlink
 				os.symlink(sub_profile_dir, os.path.join(user_config_dir, "make.profile"))
 
-				#Create minimal herds.xml
-				herds_xml = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE herds SYSTEM "http://www.gentoo.org/dtd/herds.dtd">
-<?xml-stylesheet href="/xsl/herds.xsl" type="text/xsl" ?>
-<?xml-stylesheet href="/xsl/guide.xsl" type="text/xsl" ?>
-<herds>
-<herd>
-  <name>base-system</name>
-  <email>base-system@gentoo.org</email>
-  <description>Core system utilities and libraries.</description>
-  <maintainer>
-    <email>base-system@gentoo.orgg</email>
-    <name>Base System</name>
-    <role>Base System Maintainer</role>
-  </maintainer>
-</herd>
-</herds>
-"""
-				with open(os.path.join(metadata_dir, "metadata.xml"), 'w') as f:
-					f.write(herds_xml)
-
 		make_conf = {
 			"ACCEPT_KEYWORDS": "x86",
 			"CLEAN_DELAY": "0",
-- 
2.8.1



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [gentoo-portage-dev] [PATCH 2/4] tests: Add type="" to <maintainer/>
  2016-04-16 18:06 [gentoo-portage-dev] [PATCH 0/4] GLEP 67 test cleanup & XML Schema for repoman Michał Górny
  2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 1/4] tests: Stop using herds Michał Górny
@ 2016-04-16 18:06 ` Michał Górny
  2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 3/4] repoman: Use XML Schema for metadata.xml validation Michał Górny
  2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 4/4] tests: Include metadata.xsd for repoman tests Michał Górny
  3 siblings, 0 replies; 5+ messages in thread
From: Michał Górny @ 2016-04-16 18:06 UTC (permalink / raw
  To: gentoo-portage-dev; +Cc: Michał Górny

---
 pym/portage/tests/resolver/ResolverPlayground.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py
index 68e047a..d1434f7 100644
--- a/pym/portage/tests/resolver/ResolverPlayground.py
+++ b/pym/portage/tests/resolver/ResolverPlayground.py
@@ -47,7 +47,7 @@ class ResolverPlayground(object):
 	metadata_xml_template = """<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
 <pkgmetadata>
-<maintainer>
+<maintainer type="person">
 <email>maintainer-needed@gentoo.org</email>
 <description>Description of the maintainership</description>
 </maintainer>
-- 
2.8.1



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [gentoo-portage-dev] [PATCH 3/4] repoman: Use XML Schema for metadata.xml validation
  2016-04-16 18:06 [gentoo-portage-dev] [PATCH 0/4] GLEP 67 test cleanup & XML Schema for repoman Michał Górny
  2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 1/4] tests: Stop using herds Michał Górny
  2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 2/4] tests: Add type="" to <maintainer/> Michał Górny
@ 2016-04-16 18:06 ` Michał Górny
  2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 4/4] tests: Include metadata.xsd for repoman tests Michał Górny
  3 siblings, 0 replies; 5+ messages in thread
From: Michał Górny @ 2016-04-16 18:06 UTC (permalink / raw
  To: gentoo-portage-dev; +Cc: Michał Górny

---
 pym/repoman/_xml.py                       | 16 ++++++-------
 pym/repoman/checks/ebuilds/pkgmetadata.py |  6 ++---
 pym/repoman/metadata.py                   | 39 ++++++++++++++++---------------
 pym/repoman/scanner.py                    |  8 +++----
 4 files changed, 35 insertions(+), 34 deletions(-)

diff --git a/pym/repoman/_xml.py b/pym/repoman/_xml.py
index 2661f14..026cd3a 100644
--- a/pym/repoman/_xml.py
+++ b/pym/repoman/_xml.py
@@ -12,7 +12,7 @@ from portage import os
 from portage.output import red
 from portage.process import find_binary
 
-from repoman.metadata import fetch_metadata_dtd
+from repoman.metadata import fetch_metadata_xsd
 from repoman._subprocess import repoman_getstatusoutput
 
 
@@ -53,12 +53,12 @@ class _MetadataTreeBuilder(xml.etree.ElementTree.TreeBuilder):
 
 class XmlLint(object):
 
-	def __init__(self, options, repoman_settings, metadata_dtd=None):
-		self.metadata_dtd = (metadata_dtd or
-			os.path.join(repoman_settings["DISTDIR"], 'metadata.dtd'))
+	def __init__(self, options, repoman_settings, metadata_xsd=None):
+		self.metadata_xsd = (metadata_xsd or
+			os.path.join(repoman_settings["DISTDIR"], 'metadata.xsd'))
 		self.options = options
 		self.repoman_settings = repoman_settings
-		self._is_capable = metadata_dtd is not None
+		self._is_capable = metadata_xsd is not None
 		self.binary = None
 		self._check_capable()
 
@@ -70,7 +70,7 @@ class XmlLint(object):
 			print(red("!!! xmllint not found. Can't check metadata.xml.\n"))
 			self._is_capable = False
 		elif not self._is_capable:
-			if not fetch_metadata_dtd(self.metadata_dtd, self.repoman_settings):
+			if not fetch_metadata_xsd(self.metadata_xsd, self.repoman_settings):
 				sys.exit(1)
 			# this can be problematic if xmllint changes their output
 			self._is_capable = True
@@ -94,8 +94,8 @@ class XmlLint(object):
 		# xmlint can produce garbage output even on success, so only dump
 		# the ouput when it fails.
 		st, out = repoman_getstatusoutput(
-			self.binary + " --nonet --noout --dtdvalid %s %s" % (
-				portage._shell_quote(self.metadata_dtd),
+			self.binary + " --nonet --noout --schema %s %s" % (
+				portage._shell_quote(self.metadata_xsd),
 				portage._shell_quote(
 					os.path.join(checkdir, "metadata.xml"))))
 		if st != os.EX_OK:
diff --git a/pym/repoman/checks/ebuilds/pkgmetadata.py b/pym/repoman/checks/ebuilds/pkgmetadata.py
index 74fec69..0e583fd 100644
--- a/pym/repoman/checks/ebuilds/pkgmetadata.py
+++ b/pym/repoman/checks/ebuilds/pkgmetadata.py
@@ -40,20 +40,20 @@ from repoman._xml import _XMLParser, _MetadataTreeBuilder, XmlLint
 class PkgMetadata(object):
 	'''Package metadata.xml checks'''
 
-	def __init__(self, options, qatracker, repoman_settings, metadata_dtd=None):
+	def __init__(self, options, qatracker, repoman_settings, metadata_xsd=None):
 		'''PkgMetadata init function
 
 		@param options: ArgumentParser.parse_known_args(argv[1:]) options
 		@param qatracker: QATracker instance
 		@param repoman_settings: settings instance
-		@param metadata_dtd: path of metadata.dtd
+		@param metadata_xsd: path of metadata.xsd
 		'''
 		self.options = options
 		self.qatracker = qatracker
 		self.repoman_settings = repoman_settings
 		self.musedict = {}
 		self.xmllint = XmlLint(self.options, self.repoman_settings,
-			metadata_dtd=metadata_dtd)
+			metadata_xsd=metadata_xsd)
 
 	def check(self, xpkg, checkdir, checkdirlist, repolevel):
 		'''Performs the checks on the metadata.xml for the package
diff --git a/pym/repoman/metadata.py b/pym/repoman/metadata.py
index f1fa53a..147f9d0 100644
--- a/pym/repoman/metadata.py
+++ b/pym/repoman/metadata.py
@@ -32,8 +32,9 @@ metadata_xml_declaration = '<?xml version="1.0" encoding="%s"?>' \
 	% (metadata_xml_encoding,)
 metadata_doctype_name = 'pkgmetadata'
 metadata_dtd_uri = 'http://www.gentoo.org/dtd/metadata.dtd'
+metadata_xsd_uri = 'http://www.gentoo.org/xml-schema/metadata.xsd'
 # force refetch if the local copy creation time is older than this
-metadata_dtd_ctime_interval = 60 * 60 * 24 * 7  # 7 days
+metadata_xsd_ctime_interval = 60 * 60 * 24 * 7  # 7 days
 
 
 def parse_metadata_use(xml_tree):
@@ -85,36 +86,36 @@ def parse_metadata_use(xml_tree):
 	return uselist
 
 
-def fetch_metadata_dtd(metadata_dtd, repoman_settings):
+def fetch_metadata_xsd(metadata_xsd, repoman_settings):
 	"""
-	Fetch metadata.dtd if it doesn't exist or the ctime is older than
-	metadata_dtd_ctime_interval.
+	Fetch metadata.xsd if it doesn't exist or the ctime is older than
+	metadata_xsd_ctime_interval.
 	@rtype: bool
 	@return: True if successful, otherwise False
 	"""
 
 	must_fetch = True
-	metadata_dtd_st = None
+	metadata_xsd_st = None
 	current_time = int(time.time())
 	try:
-		metadata_dtd_st = os.stat(metadata_dtd)
+		metadata_xsd_st = os.stat(metadata_xsd)
 	except EnvironmentError as e:
 		if e.errno not in (errno.ENOENT, errno.ESTALE):
 			raise
 		del e
 	else:
-		# Trigger fetch if metadata.dtd mtime is old or clock is wrong.
-		if abs(current_time - metadata_dtd_st.st_ctime) \
-			< metadata_dtd_ctime_interval:
+		# Trigger fetch if metadata.xsd mtime is old or clock is wrong.
+		if abs(current_time - metadata_xsd_st.st_ctime) \
+			< metadata_xsd_ctime_interval:
 			must_fetch = False
 
 	if must_fetch:
 		print()
 		print(
-			"%s the local copy of metadata.dtd "
+			"%s the local copy of metadata.xsd "
 			"needs to be refetched, doing that now" % green("***"))
 		print()
-		parsed_url = urlparse(metadata_dtd_uri)
+		parsed_url = urlparse(metadata_xsd_uri)
 		setting = 'FETCHCOMMAND_' + parsed_url.scheme.upper()
 		fcmd = repoman_settings.get(setting)
 		if not fcmd:
@@ -124,29 +125,29 @@ def fetch_metadata_dtd(metadata_dtd, repoman_settings):
 				return False
 
 		destdir = repoman_settings["DISTDIR"]
-		fd, metadata_dtd_tmp = tempfile.mkstemp(
-			prefix='metadata.dtd.', dir=destdir)
+		fd, metadata_xsd_tmp = tempfile.mkstemp(
+			prefix='metadata.xsd.', dir=destdir)
 		os.close(fd)
 
 		try:
 			if not portage.getbinpkg.file_get(
-				metadata_dtd_uri, destdir, fcmd=fcmd,
-				filename=os.path.basename(metadata_dtd_tmp)):
+				metadata_xsd_uri, destdir, fcmd=fcmd,
+				filename=os.path.basename(metadata_xsd_tmp)):
 				logging.error(
-					"failed to fetch metadata.dtd from '%s'" % metadata_dtd_uri)
+					"failed to fetch metadata.xsd from '%s'" % metadata_xsd_uri)
 				return False
 
 			try:
 				portage.util.apply_secpass_permissions(
-					metadata_dtd_tmp,
+					metadata_xsd_tmp,
 					gid=portage.data.portage_gid, mode=0o664, mask=0o2)
 			except portage.exception.PortageException:
 				pass
 
-			os.rename(metadata_dtd_tmp, metadata_dtd)
+			os.rename(metadata_xsd_tmp, metadata_xsd)
 		finally:
 			try:
-				os.unlink(metadata_dtd_tmp)
+				os.unlink(metadata_xsd_tmp)
 			except OSError:
 				pass
 
diff --git a/pym/repoman/scanner.py b/pym/repoman/scanner.py
index 36248cb..1384a12 100644
--- a/pym/repoman/scanner.py
+++ b/pym/repoman/scanner.py
@@ -82,11 +82,11 @@ class Scanner(object):
 			portage.util.stack_lists([self.categories], incremental=1))
 		self.categories = self.repo_settings.repoman_settings.categories
 
-		metadata_dtd = None
+		metadata_xsd = None
 		for path in reversed(self.repo_settings.repo_config.eclass_db.porttrees):
-			path = os.path.join(path, 'metadata/dtd/metadata.dtd')
+			path = os.path.join(path, 'metadata/xml-schema/metadata.xsd')
 			if os.path.exists(path):
-				metadata_dtd = path
+				metadata_xsd = path
 				break
 
 		self.portdb = repo_settings.portdb
@@ -216,7 +216,7 @@ class Scanner(object):
 		self.fetchcheck = FetchChecks(
 			self.qatracker, self.repo_settings, self.portdb, self.vcs_settings)
 		self.pkgmeta = PkgMetadata(self.options, self.qatracker,
-			self.repo_settings.repoman_settings, metadata_dtd=metadata_dtd)
+			self.repo_settings.repoman_settings, metadata_xsd=metadata_xsd)
 		self.thirdparty = ThirdPartyMirrors(self.repo_settings.repoman_settings, self.qatracker)
 		self.use_flag_checks = USEFlagChecks(self.qatracker, uselist)
 		self.keywordcheck = KeywordChecks(self.qatracker, self.options)
-- 
2.8.1



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [gentoo-portage-dev] [PATCH 4/4] tests: Include metadata.xsd for repoman tests
  2016-04-16 18:06 [gentoo-portage-dev] [PATCH 0/4] GLEP 67 test cleanup & XML Schema for repoman Michał Górny
                   ` (2 preceding siblings ...)
  2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 3/4] repoman: Use XML Schema for metadata.xml validation Michał Górny
@ 2016-04-16 18:06 ` Michał Górny
  3 siblings, 0 replies; 5+ messages in thread
From: Michał Górny @ 2016-04-16 18:06 UTC (permalink / raw
  To: gentoo-portage-dev; +Cc: Michał Górny

---
 .travis.yml                              |   4 +-
 MANIFEST.in                              |   2 +-
 cnf/metadata.dtd                         | 102 ------
 cnf/metadata.xsd                         | 547 +++++++++++++++++++++++++++++++
 pym/portage/tests/repoman/test_simple.py |   8 +-
 5 files changed, 554 insertions(+), 109 deletions(-)
 delete mode 100644 cnf/metadata.dtd
 create mode 100644 cnf/metadata.xsd

diff --git a/.travis.yml b/.travis.yml
index b662d94..5213fee 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,8 +8,8 @@ python:
 script:
     - ./setup.py test
     - ./setup.py install --root=/tmp/install-root
-    # prevent repoman tests from trying to fetch metadata.dtd
+    # prevent repoman tests from trying to fetch metadata.xsd
     - mkdir -p /tmp/install-root/usr/lib/portage/cnf
-    - cp cnf/metadata.dtd /tmp/install-root/usr/lib/portage/cnf/
+    - cp cnf/metadata.xsd /tmp/install-root/usr/lib/portage/cnf/
     - sudo rsync -a /tmp/install-root/. /
     - python -b -Wd -m portage.tests.runTests
diff --git a/MANIFEST.in b/MANIFEST.in
index d65c874..2178460 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -12,7 +12,7 @@ include cnf/make.conf.example.*
 
 # extra files for tests
 include .portage_not_installed
-include cnf/metadata.dtd
+include cnf/metadata.xsd
 
 # extra scripts
 include misc/*
diff --git a/cnf/metadata.dtd b/cnf/metadata.dtd
deleted file mode 100644
index ff2649c..0000000
--- a/cnf/metadata.dtd
+++ /dev/null
@@ -1,102 +0,0 @@
-<!ELEMENT packages ( pkgmetadata* )>
-
-<!-- Metadata for a category -->
-<!ELEMENT catmetadata ( (longdescription)* )>
-<!ATTLIST catmetadata pkgname CDATA "">
-
-<!-- Metadata for a package -->
-<!ELEMENT pkgmetadata ( (herd|maintainer|natural-name|longdescription|use|upstream)* )>
-<!ATTLIST pkgmetadata pkgname CDATA "">
-
-  <!-- One tag for each herd this package is assigned to. -->
-  <!ELEMENT herd (#PCDATA)>
-
-  <!-- One tag for each maintainer of a package, multiple allowed-->
-  <!ELEMENT maintainer ( email, (description| name)* )>
-
-  <!-- Natural name for package, example: LibreOffice (for app-office/libreoffice) -->
-  <!ELEMENT natural-name (#PCDATA) >
-
-  <!-- A long description of the package in freetext-->
-  <!ELEMENT longdescription (#PCDATA|pkg|cat)* >
-
-  <!-- The changelog of the package-->
-  <!-- Please note that #PCDATA is mentioned only for the upstream changelog
-    element, where the content is a URL. This is due to limitations of the DTD -->
-  <!ELEMENT changelog (#PCDATA|change)* >
-    <!-- The changelog contains various "changes"-->
-    <!ELEMENT change (date,(developer|version|description|file|contributor|bug)*) >
-      <!ELEMENT date (#PCDATA) >
-        <!-- The date of the change, in "YYYY-MM-DD" format -->
-      <!ELEMENT developer (name?,email) >
-        <!-- The developer that made the change. The email is required, name
-	   is optional-->
-      <!ELEMENT version (#PCDATA) >
-        <!--version of the packages involved (one tag per version)-->
-      <!ELEMENT file (#PCDATA) ><!-- one tag per file touched -->
-      <!ELEMENT contributor (name?,email?) >
-        <!-- A reference to a user that helped in causing this change. 
-          There should at least be a name or email address included. 
-          Email is preferred -->
-      <!ELEMENT bug (#PCDATA) >
-        <!-- bug-id of a bug fixed by this change, multiple allowed. The 
-          format of this is a number or alias for a bug. NOT including a 
-          # character -->
-
-  <!-- description of what this USE flag does for this package -->
-  <!ELEMENT use (flag)* >
-    <!ELEMENT flag (#PCDATA|pkg|cat)* >
-      <!-- name attribute holds the name of the USE flag -->
-      <!ATTLIST flag name CDATA #REQUIRED >
-
-  <!-- upstream metadata information (maintainers, upstream docs,..) -->
-  <!ELEMENT upstream (maintainer|changelog|doc|bugs-to|remote-id)* >
-    <!-- Due to the limitation of DTD this will also allow a status
-      attribute for the package maintainer element. Please note that
-      the usage of the status attribute is nevertheless _only_ allowed
-      in the upstream maintainer element. -->
-    <!ATTLIST maintainer status (active|inactive|unknown) "unknown" >
-    <!-- URL where the location of the upstream documentation can be found -->
-    <!ELEMENT doc (#PCDATA)>
-      <!ATTLIST doc lang CDATA "C" >
-    <!-- location where to report bugs
-      (may also be an email address prefixed with mailto:) -->
-    <!ELEMENT bugs-to (#PCDATA)>
-    <!-- specify a type of package identification tracker -->
-    <!ELEMENT remote-id (#PCDATA)>
-      <!ATTLIST remote-id type (bitbucket|cpan|cpan-module|cpe|cran|ctan|freecode|freshmeat|github|gitorious|google-code|launchpad|pear|pecl|pypi|rubyforge|rubygems|sourceforge|sourceforge-jp|vim) #REQUIRED>
-
-  <!-- category/package information for cross-linking in descriptions
-    and useflag descriptions -->
-  <!ELEMENT pkg (#PCDATA) >
-  <!ELEMENT cat (#PCDATA) >
-          
-<!-- Common attributes -->
-
-<!-- the lang attribute, specifies the language of this tag. This is 
-  only useful for descriptions of various kinds. If a tag with this 
-  attribute is included there must be a description in the default 
-  language "C" or "en", which is equivalent -->
-  <!ATTLIST description lang CDATA "C" >
-  <!ATTLIST longdescription lang CDATA "C" >
-  <!ATTLIST use lang CDATA "C" >
-
-<!-- The restrict attribute, this attribute specifies restrictions on 
-  the applicability of tags on versions. The format of this attribute is 
-  equal to the format of DEPEND lines in ebuilds. There is one special 
-  value though: restrict="*". A tag that specifies this only applies if 
-  there are no other tags that apply.
-  
-  For required tags, there must be either an unrestricted version, or a 
-  version that is default restricted. -->
-  <!ATTLIST herd restrict CDATA #IMPLIED >
-  <!ATTLIST maintainer restrict CDATA #IMPLIED >
-  <!ATTLIST longdescription restrict CDATA #IMPLIED >
-  <!ATTLIST flag restrict CDATA #IMPLIED >
-
-
-
-<!-- standard parts -->
-<!ELEMENT email (#PCDATA) ><!-- an email address -->
-<!ELEMENT name (#PCDATA) ><!-- the name of a person (maintainer, contributor)-->
-<!ELEMENT description (#PCDATA) ><!-- A description of a maintainer or change-->
diff --git a/cnf/metadata.xsd b/cnf/metadata.xsd
new file mode 100644
index 0000000..0ead09e
--- /dev/null
+++ b/cnf/metadata.xsd
@@ -0,0 +1,547 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'>
+	<!-- top-level variants -->
+	<xs:element name='pkgmetadata' type='pkgMetadataType'>
+		<!-- note: restrict uniquity rules are simplified
+			 the spec says: one for each matched package
+			 we can only do: one for each restrict rule -->
+		<xs:unique name='longDescUniquityConstraint'>
+			<xs:selector xpath='longdescription'/>
+			<xs:field xpath='@lang'/>
+			<xs:field xpath='@restrict'/>
+		</xs:unique>
+		<xs:unique name='maintainerUniquityConstraint'>
+			<xs:selector xpath='maintainer'/>
+			<xs:field xpath='email'/>
+			<xs:field xpath='@restrict'/>
+		</xs:unique>
+		<xs:unique name='slotsUniquityConstraint'>
+			<xs:selector xpath='slots'/>
+			<xs:field xpath='@lang'/>
+		</xs:unique>
+		<xs:unique name='upstreamSingleConstraint'>
+			<xs:selector xpath='upstream'/>
+			<xs:field xpath='@fake-only-once'/>
+		</xs:unique>
+		<xs:unique name='useUniquityConstraint'>
+			<xs:selector xpath='use'/>
+			<xs:field xpath='@lang'/>
+		</xs:unique>
+	</xs:element>
+	<xs:element name='catmetadata' type='catMetadataType'>
+		<xs:unique name='catLongDescUniquityConstraint'>
+			<xs:selector xpath='longdescription'/>
+			<xs:field xpath='@lang'/>
+		</xs:unique>
+	</xs:element>
+
+	<!-- global elements -->
+	<xs:complexType name='pkgMetadataType'>
+		<xs:choice minOccurs='0' maxOccurs='unbounded'>
+			<xs:element name='longdescription' type='longDescType'/>
+			<xs:element name='maintainer' type='maintainerType'>
+				<xs:unique name='maintainerDescUniquityConstraint'>
+					<xs:selector xpath='description'/>
+					<xs:field xpath='@lang'/>
+				</xs:unique>
+			</xs:element>
+			<xs:element name='slots' type='slotsType'>
+				<xs:unique name='slotUniquityConstraint'>
+					<xs:selector xpath='slot'/>
+					<xs:field xpath='@name'/>
+				</xs:unique>
+				<xs:unique name='subslotsSingleConstraint'>
+					<xs:selector xpath='subslots'/>
+					<xs:field xpath='@fake-only-once'/>
+				</xs:unique>
+			</xs:element>
+			<xs:element name='upstream' type='upstreamType'>
+				<xs:unique name='bugsToSingleConstraint'>
+					<xs:selector xpath='bugs-to'/>
+					<xs:field xpath='@fake-only-once'/>
+				</xs:unique>
+				<xs:unique name='changelogSingleConstraint'>
+					<xs:selector xpath='changelog'/>
+					<xs:field xpath='@fake-only-once'/>
+				</xs:unique>
+				<!-- prevent accidentally repeating the same remote -->
+				<xs:unique name='upstreamRemoteIdRepetitionConstraint'>
+					<xs:selector xpath='remote-id'/>
+					<xs:field xpath='@type'/>
+					<xs:field xpath='.'/>
+				</xs:unique>
+				<xs:unique name='upstreamDocUniquityConstraint'>
+					<xs:selector xpath='doc'/>
+					<xs:field xpath='@lang'/>
+				</xs:unique>
+			</xs:element>
+			<xs:element name='use' type='useType'>
+				<xs:unique name='flagUniquityConstraint'>
+					<xs:selector xpath='flag'/>
+					<xs:field xpath='@name'/>
+					<xs:field xpath='@restrict'/>
+				</xs:unique>
+			</xs:element>
+		</xs:choice>
+	</xs:complexType>
+
+	<xs:complexType name='catMetadataType'>
+		<xs:choice minOccurs='0' maxOccurs='unbounded'>
+			<xs:element name='longdescription' type='catLongDescType'/>
+		</xs:choice>
+	</xs:complexType>
+
+	<!-- the huge <upstream/> structure -->
+	<xs:complexType name='upstreamType'>
+		<xs:choice minOccurs='0' maxOccurs='unbounded'>
+			<xs:element name='maintainer' type='upstreamMaintainerType'>
+				<xs:unique name='upstreamMaintainerUniquityConstraint'>
+					<xs:selector xpath='maintainer'/>
+					<xs:field xpath='name'/>
+				</xs:unique>
+			</xs:element>
+			<xs:element name='changelog' type='urlOnceType'/>
+			<xs:element name='doc' type='upstreamDocType'/>
+			<xs:element name='bugs-to' type='urlOnceType'/>
+			<xs:element name='remote-id' type='upstreamRemoteIdType'/>
+		</xs:choice>
+		<xs:attribute name='fake-only-once'
+			fixed='there can be at most one &lt;upstream/&gt; element'/>
+	</xs:complexType>
+
+	<!-- maintainer in two variants -->
+	<xs:complexType name='maintainerType'>
+		<xs:all>
+			<xs:element name='email' type='emailType'
+				minOccurs='1'/>
+			<xs:element name='name' type='xs:token'
+				minOccurs='0'/>
+			<xs:element name='description' type='maintainerDescType'
+				minOccurs='0'/>
+		</xs:all>
+		<xs:attribute name='type' type='maintainerTypeAttrType'
+			use='required'/>
+		<xs:attribute name='restrict' type='restrictAttrType'/>
+	</xs:complexType>
+
+	<xs:simpleType name='maintainerTypeAttrType'>
+		<xs:restriction base='xs:token'>
+			<xs:enumeration value='person'/>
+			<xs:enumeration value='project'/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<xs:complexType name='upstreamMaintainerType'>
+		<xs:all>
+			<xs:element name='email' type='emailType'
+				minOccurs='0'/>
+			<xs:element name='name' type='xs:token'
+				minOccurs='0'/>
+		</xs:all>
+		<xs:attribute name='status' type='upstreamMaintainerStatusAttrType'
+			default='unknown'/>
+	</xs:complexType>
+
+	<xs:simpleType name='upstreamMaintainerStatusAttrType'>
+		<xs:restriction base='xs:token'>
+			<xs:enumeration value='active'/>
+			<xs:enumeration value='inactive'/>
+			<xs:enumeration value='unknown'/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<xs:complexType name='maintainerDescType'>
+		<xs:simpleContent>
+			<xs:extension base="xs:token">
+				<xs:attribute name='lang' type='langAttrType' default='en'/>
+			</xs:extension>
+		</xs:simpleContent>
+	</xs:complexType>
+
+	<!-- long description -->
+	<xs:complexType name='longDescType' mixed='true'>
+		<xs:choice minOccurs='0' maxOccurs='unbounded'>
+			<xs:element name='pkg' type='pkgType'/>
+			<xs:element name='cat' type='catType'/>
+		</xs:choice>
+		<xs:attribute name='lang' type='langAttrType' default='en'/>
+		<xs:attribute name='restrict' type='restrictAttrType'/>
+	</xs:complexType>
+
+	<xs:complexType name='catLongDescType' mixed='true'>
+		<xs:choice minOccurs='0' maxOccurs='unbounded'>
+			<xs:element name='pkg' type='pkgType'/>
+			<xs:element name='cat' type='catType'/>
+		</xs:choice>
+		<xs:attribute name='lang' type='langAttrType' default='en'/>
+	</xs:complexType>
+
+	<!-- slots -->
+	<xs:complexType name='slotsType'>
+		<xs:choice minOccurs='0' maxOccurs='unbounded'>
+			<xs:element name='slot' type='slotType'/>
+			<xs:element name='subslots' type='tokenOnceType'/>
+		</xs:choice>
+		<xs:attribute name='lang' type='langAttrType' default='en'/>
+	</xs:complexType>
+
+	<xs:complexType name='slotType'>
+		<xs:simpleContent>
+			<xs:extension base="xs:token">
+				<xs:attribute name='name' type='slotNameAttrType'
+					use='required'/>
+			</xs:extension>
+		</xs:simpleContent>
+	</xs:complexType>
+
+	<xs:simpleType name='slotNameAttrType'>
+		<xs:restriction base='xs:token'>
+			<!-- PMS 3.1.3 Slot Names + special value '*' -->
+			<xs:pattern value="[A-Za-z0-9_][A-Za-z0-9+_.-]*|[*]"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- use flags -->
+	<xs:complexType name='useType'>
+		<xs:choice minOccurs='0' maxOccurs='unbounded'>
+			<xs:element name='flag' type='flagType'/>
+		</xs:choice>
+		<xs:attribute name='lang' type='langAttrType' default='en'/>
+	</xs:complexType>
+
+	<xs:complexType name='flagType' mixed='true'>
+		<xs:choice minOccurs='0' maxOccurs='unbounded'>
+			<xs:element name='cat' type='catType'/>
+			<xs:element name='pkg' type='pkgType'/>
+		</xs:choice>
+		<xs:attribute name='name' type='flagNameAttrType'
+			use='required'/>
+		<xs:attribute name='restrict' type='restrictAttrType'
+			default=''/>
+	</xs:complexType>
+
+	<xs:simpleType name='flagNameAttrType'>
+		<xs:restriction base='xs:token'>
+			<!-- PMS 3.1.4 USE Flag Names -->
+			<xs:pattern value="[A-Za-z0-9][A-Za-z0-9+_@-]*"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- upstream-specific types -->
+	<xs:complexType name='upstreamDocType'>
+		<xs:simpleContent>
+			<xs:extension base="urlType">
+				<xs:attribute name='lang' type='langAttrType' default='en'/>
+			</xs:extension>
+		</xs:simpleContent>
+	</xs:complexType>
+
+	<xs:complexType name='upstreamRemoteIdType'>
+		<xs:simpleContent>
+			<xs:extension base="xs:token">
+				<xs:attribute name='type' type='upstreamRemoteIdTypeAttrType'
+					use='required'/>
+			</xs:extension>
+		</xs:simpleContent>
+	</xs:complexType>
+
+	<xs:simpleType name='upstreamRemoteIdTypeAttrType'>
+		<xs:restriction base='xs:token'>
+			<xs:enumeration value='bitbucket'/>
+			<xs:enumeration value='cpan'/>
+			<xs:enumeration value='cpan-module'/>
+			<xs:enumeration value='cpe'/>
+			<xs:enumeration value='cran'/>
+			<xs:enumeration value='ctan'/>
+			<xs:enumeration value='freecode'/>
+			<xs:enumeration value='freshmeat'/>
+			<xs:enumeration value='github'/>
+			<xs:enumeration value='gitlab'/>
+			<xs:enumeration value='gitorious'/>
+			<xs:enumeration value='google-code'/>
+			<xs:enumeration value='launchpad'/>
+			<xs:enumeration value='pear'/>
+			<xs:enumeration value='pecl'/>
+			<xs:enumeration value='pypi'/>
+			<xs:enumeration value='rubyforge'/>
+			<xs:enumeration value='rubygems'/>
+			<xs:enumeration value='sourceforge'/>
+			<xs:enumeration value='sourceforge-jp'/>
+			<xs:enumeration value='vim'/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- creepy mixed-text types -->
+	<xs:simpleType name='catType'>
+		<xs:restriction base='xs:token'>
+			<!-- PMS 3.1.1 Category Names -->
+			<xs:pattern value="[A-Za-z0-9_][A-Za-z0-9+_.-]*"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name='pkgType'>
+		<xs:restriction base='xs:token'>
+			<!-- PMS 3.1.1 Category Names + 3.1.2 Package Names -->
+			<!-- note: this does not enforce the 'anything matching
+				 the version syntax' requirement -->
+			<xs:pattern
+				value="[A-Za-z0-9_][A-Za-z0-9+_.-]*/[A-Za-z0-9_][A-Za-z0-9+_-]*"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- common attributes -->
+	<xs:simpleType name='langAttrType'>
+		<xs:restriction base='xs:token'>
+			<!-- ISO 639-1 language codes -->
+			<xs:enumeration value='aa'/>
+			<xs:enumeration value='ab'/>
+			<xs:enumeration value='ae'/>
+			<xs:enumeration value='af'/>
+			<xs:enumeration value='ak'/>
+			<xs:enumeration value='am'/>
+			<xs:enumeration value='an'/>
+			<xs:enumeration value='ar'/>
+			<xs:enumeration value='as'/>
+			<xs:enumeration value='av'/>
+			<xs:enumeration value='ay'/>
+			<xs:enumeration value='az'/>
+			<xs:enumeration value='ba'/>
+			<xs:enumeration value='be'/>
+			<xs:enumeration value='bg'/>
+			<xs:enumeration value='bh'/>
+			<xs:enumeration value='bi'/>
+			<xs:enumeration value='bm'/>
+			<xs:enumeration value='bn'/>
+			<xs:enumeration value='bo'/>
+			<xs:enumeration value='bo'/>
+			<xs:enumeration value='br'/>
+			<xs:enumeration value='bs'/>
+			<xs:enumeration value='ca'/>
+			<xs:enumeration value='ce'/>
+			<xs:enumeration value='ch'/>
+			<xs:enumeration value='co'/>
+			<xs:enumeration value='cr'/>
+			<xs:enumeration value='cs'/>
+			<xs:enumeration value='cs'/>
+			<xs:enumeration value='cu'/>
+			<xs:enumeration value='cv'/>
+			<xs:enumeration value='cy'/>
+			<xs:enumeration value='cy'/>
+			<xs:enumeration value='da'/>
+			<xs:enumeration value='de'/>
+			<xs:enumeration value='de'/>
+			<xs:enumeration value='dv'/>
+			<xs:enumeration value='dz'/>
+			<xs:enumeration value='ee'/>
+			<xs:enumeration value='el'/>
+			<xs:enumeration value='el'/>
+			<xs:enumeration value='en'/>
+			<xs:enumeration value='eo'/>
+			<xs:enumeration value='es'/>
+			<xs:enumeration value='et'/>
+			<xs:enumeration value='eu'/>
+			<xs:enumeration value='eu'/>
+			<xs:enumeration value='fa'/>
+			<xs:enumeration value='fa'/>
+			<xs:enumeration value='ff'/>
+			<xs:enumeration value='fi'/>
+			<xs:enumeration value='fj'/>
+			<xs:enumeration value='fo'/>
+			<xs:enumeration value='fr'/>
+			<xs:enumeration value='fr'/>
+			<xs:enumeration value='fy'/>
+			<xs:enumeration value='ga'/>
+			<xs:enumeration value='ga'/>
+			<xs:enumeration value='Ga'/>
+			<xs:enumeration value='gd'/>
+			<xs:enumeration value='gl'/>
+			<xs:enumeration value='gn'/>
+			<xs:enumeration value='gu'/>
+			<xs:enumeration value='gv'/>
+			<xs:enumeration value='ha'/>
+			<xs:enumeration value='he'/>
+			<xs:enumeration value='hi'/>
+			<xs:enumeration value='ho'/>
+			<xs:enumeration value='hr'/>
+			<xs:enumeration value='ht'/>
+			<xs:enumeration value='hu'/>
+			<xs:enumeration value='hy'/>
+			<xs:enumeration value='hy'/>
+			<xs:enumeration value='hz'/>
+			<xs:enumeration value='ia'/>
+			<xs:enumeration value='id'/>
+			<xs:enumeration value='ie'/>
+			<xs:enumeration value='ig'/>
+			<xs:enumeration value='ii'/>
+			<xs:enumeration value='ik'/>
+			<xs:enumeration value='io'/>
+			<xs:enumeration value='is'/>
+			<xs:enumeration value='is'/>
+			<xs:enumeration value='it'/>
+			<xs:enumeration value='iu'/>
+			<xs:enumeration value='ja'/>
+			<xs:enumeration value='jv'/>
+			<xs:enumeration value='ka'/>
+			<xs:enumeration value='ka'/>
+			<xs:enumeration value='kg'/>
+			<xs:enumeration value='ki'/>
+			<xs:enumeration value='kj'/>
+			<xs:enumeration value='kk'/>
+			<xs:enumeration value='kl'/>
+			<xs:enumeration value='km'/>
+			<xs:enumeration value='kn'/>
+			<xs:enumeration value='ko'/>
+			<xs:enumeration value='kr'/>
+			<xs:enumeration value='ks'/>
+			<xs:enumeration value='ku'/>
+			<xs:enumeration value='kv'/>
+			<xs:enumeration value='kw'/>
+			<xs:enumeration value='ky'/>
+			<xs:enumeration value='la'/>
+			<xs:enumeration value='lb'/>
+			<xs:enumeration value='lg'/>
+			<xs:enumeration value='li'/>
+			<xs:enumeration value='ln'/>
+			<xs:enumeration value='lo'/>
+			<xs:enumeration value='lt'/>
+			<xs:enumeration value='lu'/>
+			<xs:enumeration value='lv'/>
+			<xs:enumeration value='mg'/>
+			<xs:enumeration value='mh'/>
+			<xs:enumeration value='mi'/>
+			<xs:enumeration value='mi'/>
+			<xs:enumeration value='mk'/>
+			<xs:enumeration value='mk'/>
+			<xs:enumeration value='ml'/>
+			<xs:enumeration value='mn'/>
+			<xs:enumeration value='mr'/>
+			<xs:enumeration value='ms'/>
+			<xs:enumeration value='ms'/>
+			<xs:enumeration value='mt'/>
+			<xs:enumeration value='my'/>
+			<xs:enumeration value='my'/>
+			<xs:enumeration value='na'/>
+			<xs:enumeration value='nb'/>
+			<xs:enumeration value='nd'/>
+			<xs:enumeration value='ne'/>
+			<xs:enumeration value='ng'/>
+			<xs:enumeration value='nl'/>
+			<xs:enumeration value='nl'/>
+			<xs:enumeration value='nn'/>
+			<xs:enumeration value='no'/>
+			<xs:enumeration value='nr'/>
+			<xs:enumeration value='nv'/>
+			<xs:enumeration value='ny'/>
+			<xs:enumeration value='oc'/>
+			<xs:enumeration value='oj'/>
+			<xs:enumeration value='om'/>
+			<xs:enumeration value='or'/>
+			<xs:enumeration value='os'/>
+			<xs:enumeration value='pa'/>
+			<xs:enumeration value='pi'/>
+			<xs:enumeration value='pl'/>
+			<xs:enumeration value='ps'/>
+			<xs:enumeration value='pt'/>
+			<xs:enumeration value='qu'/>
+			<xs:enumeration value='rm'/>
+			<xs:enumeration value='rn'/>
+			<xs:enumeration value='ro'/>
+			<xs:enumeration value='ro'/>
+			<xs:enumeration value='ru'/>
+			<xs:enumeration value='rw'/>
+			<xs:enumeration value='sa'/>
+			<xs:enumeration value='sc'/>
+			<xs:enumeration value='sd'/>
+			<xs:enumeration value='se'/>
+			<xs:enumeration value='sg'/>
+			<xs:enumeration value='si'/>
+			<xs:enumeration value='sk'/>
+			<xs:enumeration value='sk'/>
+			<xs:enumeration value='sl'/>
+			<xs:enumeration value='sm'/>
+			<xs:enumeration value='sn'/>
+			<xs:enumeration value='so'/>
+			<xs:enumeration value='sq'/>
+			<xs:enumeration value='sq'/>
+			<xs:enumeration value='sr'/>
+			<xs:enumeration value='ss'/>
+			<xs:enumeration value='st'/>
+			<xs:enumeration value='su'/>
+			<xs:enumeration value='sv'/>
+			<xs:enumeration value='sw'/>
+			<xs:enumeration value='ta'/>
+			<xs:enumeration value='te'/>
+			<xs:enumeration value='tg'/>
+			<xs:enumeration value='th'/>
+			<xs:enumeration value='ti'/>
+			<xs:enumeration value='tk'/>
+			<xs:enumeration value='tl'/>
+			<xs:enumeration value='tn'/>
+			<xs:enumeration value='to'/>
+			<xs:enumeration value='tr'/>
+			<xs:enumeration value='ts'/>
+			<xs:enumeration value='tt'/>
+			<xs:enumeration value='tw'/>
+			<xs:enumeration value='ty'/>
+			<xs:enumeration value='ug'/>
+			<xs:enumeration value='uk'/>
+			<xs:enumeration value='ur'/>
+			<xs:enumeration value='uz'/>
+			<xs:enumeration value='ve'/>
+			<xs:enumeration value='vi'/>
+			<xs:enumeration value='vo'/>
+			<xs:enumeration value='wa'/>
+			<xs:enumeration value='wo'/>
+			<xs:enumeration value='xh'/>
+			<xs:enumeration value='yi'/>
+			<xs:enumeration value='yo'/>
+			<xs:enumeration value='za'/>
+			<xs:enumeration value='zh'/>
+			<xs:enumeration value='zh'/>
+			<xs:enumeration value='zu'/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<xs:simpleType name='restrictAttrType'>
+		<xs:restriction base='xs:token'>
+			<!-- simplified package dependency syntax -->
+			<!-- note: 'pure' package atom is technically valid too
+				 but not really meaningful -->
+			<xs:pattern
+				value="(([&lt;&gt;]=?|[=~])[A-Za-z0-9_][A-Za-z0-9+_.-]*/[A-Za-z0-9_][A-Za-z0-9+_-]*-[0-9]+(\.[0-9]+)*[a-z]?((_alpha|_beta|_pre|_rc|_p)[0-9]*)*(-r[0-9]+)?\*?)?"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<!-- generic types -->
+	<xs:simpleType name='emailType'>
+		<xs:restriction base='xs:token'>
+			<!-- minimal safe regex -->
+			<xs:pattern value="[^@]+@[^.]+\..+"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<xs:complexType name='tokenOnceType'>
+		<xs:simpleContent>
+			<xs:extension base="xs:token">
+				<xs:attribute name='fake-only-once'
+					fixed='there can be at most one element of this type'/>
+			</xs:extension>
+		</xs:simpleContent>
+	</xs:complexType>
+
+	<xs:simpleType name='urlType'>
+		<xs:restriction base='xs:token'>
+			<!-- TODO: something better? -->
+			<xs:pattern value="(mailto:[^@]+@[^.]+\..+|https?://.+)"/>
+		</xs:restriction>
+	</xs:simpleType>
+
+	<xs:complexType name='urlOnceType'>
+		<xs:simpleContent>
+			<xs:extension base="urlType">
+				<xs:attribute name='fake-only-once'
+					fixed='there can be at most one element of this type'/>
+			</xs:extension>
+		</xs:simpleContent>
+	</xs:complexType>
+</xs:schema>
diff --git a/pym/portage/tests/repoman/test_simple.py b/pym/portage/tests/repoman/test_simple.py
index 720560b..6a79761 100644
--- a/pym/portage/tests/repoman/test_simple.py
+++ b/pym/portage/tests/repoman/test_simple.py
@@ -128,7 +128,7 @@ class SimpleRepomanTestCase(TestCase):
 		}
 		licenses = ["GPL-2"]
 		arch_list = ["x86"]
-		metadata_dtd = os.path.join(PORTAGE_BASE_PATH, "cnf/metadata.dtd")
+		metadata_xsd = os.path.join(PORTAGE_BASE_PATH, "cnf/metadata.xsd")
 		metadata_xml_files = (
 			(
 				"dev-libs/A",
@@ -269,9 +269,9 @@ class SimpleRepomanTestCase(TestCase):
 			# involving canonical vs. non-canonical paths.
 			test_repo_symlink = os.path.join(eroot, "test_repo_symlink")
 			os.symlink(test_repo_location, test_repo_symlink)
-			metadata_dtd_dest = os.path.join(test_repo_location, 'metadata/dtd/metadata.dtd')
-			os.makedirs(os.path.dirname(metadata_dtd_dest))
-			os.symlink(metadata_dtd, metadata_dtd_dest)
+			metadata_xsd_dest = os.path.join(test_repo_location, 'metadata/xml-schema/metadata.xsd')
+			os.makedirs(os.path.dirname(metadata_xsd_dest))
+			os.symlink(metadata_xsd, metadata_xsd_dest)
 
 			if debug:
 				# The subprocess inherits both stdout and stderr, for
-- 
2.8.1



^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2016-04-16 18:07 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-04-16 18:06 [gentoo-portage-dev] [PATCH 0/4] GLEP 67 test cleanup & XML Schema for repoman Michał Górny
2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 1/4] tests: Stop using herds Michał Górny
2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 2/4] tests: Add type="" to <maintainer/> Michał Górny
2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 3/4] repoman: Use XML Schema for metadata.xml validation Michał Górny
2016-04-16 18:06 ` [gentoo-portage-dev] [PATCH 4/4] tests: Include metadata.xsd for repoman tests Michał Górny

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox