[PATCH v1 3/5] maintainer_utils: Move DCO entries outside of roles.

Richard Earnshaw via Sourceware Forge <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <bmm.hlit9bvuc0.gcc.gcc.rearnsha.222.1.3@forge-stage.sourceware.org>
From: Richard Earnshaw <[email protected]>

Logically, DCO entries are just another list of email addresses; they
aren't really roles within the project.  This patch splits them out of
the roles entry into their own list.  It does make it a bit more work to
re-generate the MAINTAINERS file, but it isn't enough to continue using
the existing structure.  While there, add some additional validation of
DCO entries: addresses used for a DCO must either be in the list of
active emails or the list of inactive ones.

contrib/ChangeLog:

	* maintainer_utils.py (maintainer_schema): Move the DCO data
	to a top-level user list.  Ensure that each user entry has at
	least one of roles and DCO.
	(_check_dco): New function.
	(validate): Use it.  Handle roles being absent.
	* gen-MAINTAINERS.py (format_output):  Scan the list of DCO
	entries if generating the DCO table.  Split the overflowing
	field logic into ...
	(paginate): ... here.
	* add-write-after.py (getuserdata): Write DCO data to new field.
	Check that DCO email address is known and add to inactive_email
	if not.

ChangeLog:

	* MAINTAINERS.yml: Move all DCO 'roles' to a separate list.
---
 MAINTAINERS.yml             | 189 +++++++++++++++++++++++-------------
 contrib/add-write-after.py  |  12 ++-
 contrib/gen-MAINTAINERS.py  |  40 +++++---
 contrib/maintainer_utils.py |  40 +++++---
 4 files changed, 189 insertions(+), 92 deletions(-)

diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml
index 5d44576d7579b..64a4d1b65e293 100644
--- a/MAINTAINERS.yml
+++ b/MAINTAINERS.yml
@@ -71,8 +71,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: soumyaa
+  DCO:
+  - [email protected]
 - sn: Arras
   cn: Paul-Antoine Arras
   email:
@@ -227,11 +228,12 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
-  - DCO: [email protected]
   account: dbarboza
   aliases:
   - Daniel Henrique Barboza
+  DCO:
+  - [email protected]
+  - [email protected]
 - sn: Bauermann
   cn: Thiago Jung Bauermann
   email:
@@ -340,11 +342,12 @@ users:
   roles:
   - Reviewer: register allocation
   - WriteAfter
-  - DCO: [email protected]
   account: bergner
   inactive_email:
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Berlin
   cn: Daniel Berlin
   email:
@@ -845,8 +848,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: dhruvc
+  DCO:
+  - [email protected]
 - sn: Chen
   cn: Dehao Chen
   email:
@@ -864,11 +868,12 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
-  - DCO: [email protected]
   account: zhongyao
   aliases:
   - zhongyao
+  DCO:
+  - [email protected]
+  - [email protected]
 - sn: Chêne
   cn: Fabien Chêne
   email:
@@ -966,8 +971,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: jchrist
+  DCO:
+  - [email protected]
 - sn: Christina
   cn: Tamar Christina
   email:
@@ -1063,10 +1069,11 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: alx
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Conner
   cn: Josh Conner
   email:
@@ -1187,8 +1194,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: peppe
+  DCO:
+  - [email protected]
 - sn: Dabbelt
   cn: Palmer Dabbelt
   email:
@@ -1238,14 +1246,15 @@ users:
   - Maintainer: riscv port
   - Reviewer: vectorizer (+ tree-if-conv)
   - WriteAfter
-  - DCO: [email protected]
-  - DCO: [email protected]
-  - DCO: [email protected]
   account: rdapp
   inactive_email:
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
+  - [email protected]
+  - [email protected]
 - sn: Dardis
   cn: Simon Dardis
   email:
@@ -1760,8 +1769,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: kael
+  DCO:
+  - [email protected]
 - sn: Franke
   cn: Daniel Franke
   email:
@@ -1915,11 +1925,12 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: pgodbole
   inactive_email:
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Granlund
   cn: Torbjorn Granlund
   email:
@@ -2064,10 +2075,11 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: eikansh
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Gupta
   cn: Vineet Gupta
   email:
@@ -2086,7 +2098,6 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: naveenh
   aliases:
   - Naveen Gowda
@@ -2096,6 +2107,8 @@ users:
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Hagog
   cn: Mostafa Hagog
   email:
@@ -2310,11 +2323,12 @@ users:
   - Maintainer: '*gimpl*'
   - Maintainer: '*vrp, ranger'
   - WriteAfter
-  - DCO: [email protected]
   account: aldyh
   inactive_email:
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Herron
   cn: Philip Herron
   email:
@@ -2727,8 +2741,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: mjires
+  DCO:
+  - [email protected]
 - sn: Johnson
   cn: Janis Johnson
   email:
@@ -3075,10 +3090,11 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: mkretz
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Krupp
   cn: Louis Krupp
   email:
@@ -3095,11 +3111,12 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: prathamesh3492
   inactive_email:
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Kumar
   cn: Venkataramanan Kumar
   email:
@@ -3202,9 +3219,10 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: tlange
   inactive: true
+  DCO:
+  - [email protected]
 - sn: Langton
   cn: Asher Langton
   email:
@@ -3266,11 +3284,6 @@ users:
   - Maintainer: scheduler (+ haifa)
     email: [email protected]
   - WriteAfter: [email protected]
-  - DCO: [email protected]
-  - DCO: [email protected]
-  - DCO: [email protected]
-  - DCO: [email protected]
-  - DCO: [email protected]
   account: law
   aliases:
   - jlaw
@@ -3279,6 +3292,12 @@ users:
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
+  - [email protected]
+  - [email protected]
+  - [email protected]
+  - [email protected]
 - sn: Lawrence
   cn: Alan Lawrence
   email:
@@ -3335,8 +3354,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: garthlei
+  DCO:
+  - [email protected]
 - sn: Lemke
   cn: James Lemke
   email:
@@ -3410,8 +3430,8 @@ users:
   cn: Jim Lin
   email:
   - [email protected]
-  roles:
-  - DCO: [email protected]
+  DCO:
+  - [email protected]
 - sn: Lin
   cn: Kewen Lin
   email:
@@ -3443,8 +3463,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: marxin
+  DCO:
+  - [email protected]
 - sn: Liu
   cn: Hao Liu
   email:
@@ -3589,7 +3610,6 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: hjl
   inactive_email:
   - [email protected]
@@ -3597,6 +3617,8 @@ users:
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Luo
   cn: Longjun Luo
   email:
@@ -3604,8 +3626,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: anatasluo
+  DCO:
+  - [email protected]
 - sn: Luo
   cn: Xiong Hu Luo
   email:
@@ -3664,8 +3687,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: yuao
+  DCO:
+  - [email protected]
 - sn: Machado
   cn: Luis Machado
   email:
@@ -3739,10 +3763,11 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: matmal01
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Maltsev
   cn: Mikhail Maltsev
   email:
@@ -3765,9 +3790,10 @@ users:
   - Maintainer: libga68
     email: [email protected]
   - WriteAfter
-  - DCO: [email protected]
   account: jemarch
   forgeid: jemarch
+  DCO:
+  - [email protected]
 - sn: Markianos-Wright
   cn: Stamatis Markianos-Wright
   email:
@@ -4033,12 +4059,13 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: mir
   inactive: true
   inactive_email:
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Miranda
   cn: Cupertino Miranda
   email:
@@ -4179,10 +4206,11 @@ users:
   roles:
   - Maintainer: Modula-2 front end
   - WriteAfter
-  - DCO: [email protected]
   account: gaius
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Müllner
   cn: Christoph Müllner
   email:
@@ -4196,9 +4224,9 @@ users:
   email:
   - [email protected]
   - [email protected]
-  roles:
-  - DCO: [email protected]
-  - DCO: [email protected]
+  DCO:
+  - [email protected]
+  - [email protected]
 - sn: Munroe
   cn: Steven Munroe
   email:
@@ -4260,11 +4288,12 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: nsz
   inactive: true
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Nascimento
   cn: Victor Do Nascimento
   email:
@@ -4565,8 +4594,9 @@ users:
   - Maintainer: Rust
   - Maintainer: libgrust
   - WriteAfter
-  - DCO: [email protected]
   account: pep
+  DCO:
+  - [email protected]
 - sn: Pavenis
   cn: Andris Pavenis
   email:
@@ -4629,10 +4659,11 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: mikpe
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Pfau
   cn: Johannes Pfau
   email:
@@ -4682,8 +4713,6 @@ users:
     email: [email protected]
   - Reviewer: aarch64 port
   - WriteAfter: [email protected]
-  - DCO: [email protected]
-  - DCO: [email protected]
   account: pinskia
   forgeid: pinskia
   inactive_email:
@@ -4694,6 +4723,9 @@ users:
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
+  - [email protected]
 - sn: Pitre
   cn: Nicolas Pitre
   email:
@@ -4778,13 +4810,14 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: siddhesh
   inactive_email:
   - [email protected]
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Praveen
   cn: Vidya Praveen
   email:
@@ -4875,7 +4908,6 @@ users:
   roles:
   - Maintainer: arm port
   - WriteAfter
-  - DCO: [email protected]
   account: ramana
   inactive_email:
   - [email protected]
@@ -4884,17 +4916,20 @@ users:
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Rahimi
   cn: Navid Rahimi
   email:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: navidr
   inactive: true
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Raj
   cn: Rishi Raj
   email:
@@ -4902,9 +4937,10 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: rshraj45035
   inactive: true
+  DCO:
+  - [email protected]
 - sn: Raman
   cn: Easwaran Raman
   email:
@@ -4998,8 +5034,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: egas
+  DCO:
+  - [email protected]
 - sn: Richards
   cn: Alfie Richards
   email:
@@ -5243,12 +5280,13 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: tbsaunde
   inactive: true
   inactive_email:
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Sawdey
   cn: Aaron Sawdey
   email:
@@ -5306,14 +5344,14 @@ users:
   cn: Bill Schmidt
   email:
   - [email protected]
-  roles:
-  - DCO: [email protected]
   inactive: true
   inactive_email:
   - [email protected]
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Schmidt
   cn: Will Schmidt
   email:
@@ -5484,8 +5522,9 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: nshead
+  DCO:
+  - [email protected]
 - sn: Shen
   cn: Tim Shen
   email:
@@ -5528,12 +5567,13 @@ users:
   - Maintainer: libcpp
   - Maintainer: gcov
   - WriteAfter
-  - DCO: [email protected]
   account: nathan
   inactive_email:
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Singhai
   cn: Sharad Singhai
   email:
@@ -5587,11 +5627,12 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: emsr
   inactive: true
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Sokolov
   cn: Anatoly Sokolov
   email:
@@ -5630,10 +5671,11 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: maskray
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Stallman
   cn: Richard Stallman
   email:
@@ -5975,11 +6017,12 @@ users:
   roles:
   - Maintainer: aarch64 port
   - WriteAfter
-  - DCO: [email protected]
   account: ktkachov
   forgeid: ktkachov
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Tobler
   cn: Andreas Tobler
   email:
@@ -6013,11 +6056,12 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: tomner
   inactive: true
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Tomsich
   cn: Philipp Tomsich
   email:
@@ -6098,12 +6142,13 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: uecker
   inactive_email:
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Ung
   cn: David Ung
   email:
@@ -6197,10 +6242,11 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: kugan
   inactive_email:
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Volkov
   cn: Artemiy Volkov
   email:
@@ -6279,7 +6325,6 @@ users:
   roles:
   - Maintainer: c++ runtime libs
   - WriteAfter
-  - DCO: [email protected]
   account: redi
   forgeid: redi
   inactive_email:
@@ -6287,6 +6332,8 @@ users:
   - [email protected]
   - [email protected]
   - [email protected]
+  DCO:
+  - [email protected]
 - sn: Walfridsson
   cn: Krister Walfridsson
   email:
@@ -6431,9 +6478,10 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
   account: alexanderw
   inactive: true
+  DCO:
+  - [email protected]
 - sn: Wielaard
   cn: Mark Wielaard
   email:
@@ -6551,8 +6599,9 @@ users:
   roles:
   - Maintainer: nds32 port
   - WriteAfter
-  - DCO: [email protected]
   account: jasonwucj
+  DCO:
+  - [email protected]
 - sn: Wu
   cn: Le-Chun Wu
   email:
@@ -6795,9 +6844,10 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
-  - DCO: [email protected]
   account: pzheng
+  DCO:
+  - [email protected]
+  - [email protected]
 - sn: Zhong
   cn: Juzhe Zhong
   email:
@@ -6835,11 +6885,12 @@ users:
   - [email protected]
   roles:
   - WriteAfter
-  - DCO: [email protected]
-  - DCO: [email protected]
   account: rzinsly
   aliases:
   - Raphael Moreira Zinsly
+  DCO:
+  - [email protected]
+  - [email protected]
 - sn: Zippel
   cn: Roman Zippel
   email:
diff --git a/contrib/add-write-after.py b/contrib/add-write-after.py
index 693bd8cd95362..c6c11bbcf5c87 100755
--- a/contrib/add-write-after.py
+++ b/contrib/add-write-after.py
@@ -107,11 +107,21 @@ def getuserdata():
         newuser['forgeid'] = forgeid
     print("If you are using a Developer Certificate of Origin (DCO)")
     print("you can add appropriate email addresses here")
+    dco = []
+    inactive_emails = []
     while (e := ask("DCO email (return to stop)", None, required=False)):
         if email_valid(e):
-            newuser['roles'].append({'DCO': e})
+            dco.append(e)
+            if e not in newuser['email']:
+                print(f"<{e}> not mentioned in the above list of emails.")
+                print("Adding it to inactive_emails list.")
+                inactive_emails.append(e)
         else:
             print ("That address does not look valid.  Ignored.")
+    if len(inactive_emails) > 0:
+        newuser['inactive_email'] = inactive_emails
+    if len(dco) > 0:
+        newuser['DCO'] = dco
     return newuser
 
 def main():
diff --git a/contrib/gen-MAINTAINERS.py b/contrib/gen-MAINTAINERS.py
index 124505d2890e2..4fcc4dcfc8527 100755
--- a/contrib/gen-MAINTAINERS.py
+++ b/contrib/gen-MAINTAINERS.py
@@ -211,6 +211,21 @@ def uni2alower(txt):
     return unidecode.unidecode(txt).lower()
 
 
+def paginate(data, widths):
+    """
+    Calculate the field widths for 'data', taking into account over-long
+    elements.
+    """
+    w = dict()
+    col = 0
+    overrun = 0
+    for field, width in widths:
+        width -= overrun
+        w[field] = max(width, 1)
+        overrun = max(len(data[field]) - width, 0)
+    return w
+
+
 def format_output(outfile, data):
     global active_only
     all_users = data['users']
@@ -229,7 +244,19 @@ def format_output(outfile, data):
             role = section['filter']['role']
             subclass = section['filter'].get('subclass')
             for u in all_users:
-                for r in filter(lambda x: role in x, u['roles']):
+                if role == 'DCO':
+                    for email in u.get('DCO', []):
+                        d = {
+                            'email': email,
+                            'account': u.get('account', '-'),
+                            'sn': u['sn'],
+                            'cn': u['cn'],
+                            'subsystem': None,
+                        }
+                        d['w'] = paginate(d, section['widths'])
+                        l.append(d)
+                    continue
+                for r in filter(lambda x: role in x, u.get('roles', [])):
                     if subclass:
                         sc = [
                             s
@@ -249,20 +276,11 @@ def format_output(outfile, data):
                         'cn': u['cn'],
                         'subsystem': r[role] if type(r) == dict else None,
                     }
-                    if role == 'DCO':
-                        d['email'] = r['DCO']
                     if role == 'WriteAfter' and type(r) == dict:
                         d['email'] = r['WriteAfter']
                     elif type(r) == dict and 'email' in r:
                         d['email'] = r['email']
-                    w = dict()
-                    col = 0
-                    overrun = 0
-                    for field, width in section['widths']:
-                        width -= overrun
-                        w[field] = max(width, 1)
-                        overrun = max(len(d[field]) - width, 0)
-                    d['w'] = w
+                    d['w'] = paginate(d, section['widths'])
                     l.append(d)
             kfn = itemgetter(*section['filter']['order'])
             for u in sorted(
diff --git a/contrib/maintainer_utils.py b/contrib/maintainer_utils.py
index 51a78ab72eec7..8199fd588758f 100755
--- a/contrib/maintainer_utils.py
+++ b/contrib/maintainer_utils.py
@@ -67,6 +67,14 @@ maintainer_schema = {
                             'format': 'email',
                         },
                     },
+                    'DCO': {
+                        'type': 'array',
+                        'items': {
+                            'type': 'string',
+                            'format': 'email',
+                        },
+                        "minItems": 1,
+                    },
                     'roles': {
                         'type': 'array',
                         'items': {
@@ -82,10 +90,6 @@ maintainer_schema = {
                                             'type': 'string',
                                             'format': 'email',
                                         },
-                                        'DCO': {
-                                            'type': 'string',
-                                            'format': 'email',
-                                        },
                                     },
                                     'additionalProperties': False,
                                 },
@@ -135,7 +139,11 @@ maintainer_schema = {
                     },
                 },
                 'additionalProperties': False,
-                'required': ['sn', 'cn', 'email', 'roles'],
+                'required': ['sn', 'cn', 'email'],
+                'anyOf': [
+                    {'required': ['roles']},
+                    {'required': ['DCO']},
+                ],
             },
         },
         'subsystems': {
@@ -236,6 +244,15 @@ def _check_schema(data):
     return
 
 
+def _check_dco(user):
+    # An email addrss in a DCO entry must also be listed in either the
+    # active emails list, or the inactive_emails list.
+    emails = set(user['email'] + user.get('inactive_email', []))
+    for dco in user['DCO']:
+        if dco not in emails:
+            _error(f"User: {user['cn']} DCO {dco} not listed in other emails")
+
+
 def validate(data):
     """Check the data against the schema and our own consistency checks"""
     _check_schema(data)
@@ -246,12 +263,17 @@ def validate(data):
     # subsystems list; Maintainer entires must also have a class entry, though
     # that is optional for Reviewers.
     for u in data['users']:
+        if 'DCO' in u:
+            _check_dco(u)
+        # The schema ensures that at least one of 'DCO' or 'roles'
+        # exists, so if roles is missing, we're done.
+        if 'roles' not in u:
+            continue
         # Users with the 'BZ' role should not have any other roles; we
         # can quickly skip the additional checks if that is the case.
         if len(u['roles']) == 1 and u['roles'][0] == 'BZ':
             continue
         seen_writeafter = False
-        only_dco = True
         for r in u['roles']:
             if isinstance(r, str):
                 if r == 'BZ':
@@ -260,14 +282,10 @@ def validate(data):
                     )
                 if r == 'WriteAfter':
                     seen_writeafter = True
-                only_dco = False
                 continue
             if 'WriteAfter' in r:
                 seen_writeafter = True
             need_class = True
-            if 'DCO' in r:
-                continue
-            only_dco = False
             n = r.get('Maintainer')
             if not n:
                 need_class = False
@@ -283,7 +301,7 @@ def validate(data):
                     _error(f"No subsystem entry for '{n}'.")
                 else:
                     _error(f"Multiple subsystem entries for '{n}'.")
-        if not seen_writeafter and not only_dco:
+        if not seen_writeafter:
             _error(f"User '{u['cn']}' lacks WriteAfter role.")
     if error_count:
         sys.exit(1)
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.