From patchwork Tue Jul 7 10:12:28 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Earnshaw via Sourceware Forge X-Patchwork-Id: 138639 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 507F94BA2E23 for ; Tue, 7 Jul 2026 10:15:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 507F94BA2E23 X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from forge-stage.sourceware.org (vm08.sourceware.org [IPv6:2620:52:6:3111::39]) by sourceware.org (Postfix) with ESMTPS id 582804BA2E25 for ; Tue, 7 Jul 2026 10:13:33 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 582804BA2E25 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=forge-stage.sourceware.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=forge-stage.sourceware.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 582804BA2E25 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2620:52:6:3111::39 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1783419213; cv=none; b=FTDuifk/Giz9DkPH7gLHRP8OLfn/d+heZHhH92TS+qU7dkDlTFjXmk7X8ttZuCALh9s4FZD2S6wkFwxPXkEAW7TU8nLfd0SvOa6KArBdbRj/RVKF5xfUVylP3sggaTXu2sqUtXXyzR1vqgmCJZFkK4dS6MH47JryTZAXhPwbqEE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1783419213; c=relaxed/simple; bh=fLSZ7hx9/s/go7XbaQRkkWjZuMt52n8FBUs3kPf6zMc=; h=From:Date:Subject:To:Message-ID; b=c8MfP9sy72I9l08N07FBqOSjAOlNDGdcn2yt3MsTEVPRS/U1YnOmr/TipnoMEb6PPnilc1BJl7F/+o+rK5GBiRXEmrvmqC6zRT7ncBz7dfQfY6Tg6pvhvIysk40QJRc8FwQzBjDdOAK0/n6BIfEfU0PQfPcGoeKmLmTSahuu11o= ARC-Authentication-Results: i=1; sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 582804BA2E25 Received: from forge-stage.sourceware.org (localhost [IPv6:::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by forge-stage.sourceware.org (Postfix) with ESMTPS id 289A843112; Tue, 07 Jul 2026 10:13:33 +0000 (UTC) From: Richard Earnshaw via Sourceware Forge Date: Tue, 07 Jul 2026 10:12:28 +0000 Subject: [PATCH v5 3/3] MAINTAINERS: Add a script to create a new entry in the mainainers data To: gcc-patches mailing list Cc: jefflaw@qti.qualcomm.com Message-ID: X-Mailer: batrachomyomachia X-Requested-Reviewer: rdfm X-Pull-Request-Organization: gcc X-Pull-Request-Repository: gcc-TEST X-Pull-Request: https://forge.sourceware.org/gcc/gcc-TEST/pulls/163 References: In-Reply-To: X-Patch-URL: https://forge.sourceware.org/rearnsha/gcc-TEST/commit/c963db2262568530628f406d3776b9e4ca3831f1 X-Spam-Status: No, score=-9.4 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_STATUS, SPF_HELO_PASS, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: gcc-patches mailing list , jefflaw@qti.qualcomm.com, rearnsha@arm.com Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org From: Richard Earnshaw This script is intended to help with the most common case of creating a new entry in the MAINTAINERS.yml data - adding the basic data for a new account. It takes care of creating the entry in the canonical location within the file and collecting the basic data. It only adds the write-after role, but it should be trivial to add any additional roles if necessary following the examples elsewhere. contrib/ChangeLog: * add-write-after.py: New file. --- contrib/add-write-after.py | 150 +++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100755 contrib/add-write-after.py diff --git a/contrib/add-write-after.py b/contrib/add-write-after.py new file mode 100755 index 000000000000..9c93324bc909 --- /dev/null +++ b/contrib/add-write-after.py @@ -0,0 +1,150 @@ +#! /usr/bin/env python3 + +# Add a new Write-After account to MAINTAINERS.yml + +# Copyright (C) 2026 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING. If not, write to +# the Free Software Foundation, 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import os +import pwd +import re +import sys +import unidecode +import yaml + +from optparse import OptionParser + +import maintainer_utils as maintutils + +def unilower(txt): + """return a lower-case version of txt, mapping accented characters + onto their ASCII near equivalents.""" + return unidecode.unidecode(txt).lower() + +def get_surname(name): + parts = name.split() + surname = parts[-1] + + if surname.startswith("d'"): + surname = surname[2:] + + return surname + +def ask(prompt, default, required=True): + while True: + print(f"{prompt} [{default if default else '(no default)'}]: ", end="", + flush=True) + result = sys.stdin.readline().strip() + if result != "": + return result + if default or not required: + return default + +def email_valid(e): + template = r"^[A-Za-z0-9._%+-]+@(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,}$" + if re.match(template, e): + return True + return False + +def getuserdata(): + user = pwd.getpwuid(os.getuid()) + # Make an initial guess that the name and GCC account will match + # the local account name. + # GECOS fields sometime contain a comma-separated list; in that case + # we only want the first component (the user's name) + print ("Please enter the details of your Sourceware account.") + print ("Some details have been guessed, based on your local account,") + print ("but you can adjust as required.") + newuser = { + 'sn': "", + 'cn': "", + 'email': [], + 'roles': [], + 'account': "", + } + newuser['cn'] = ask("Your name", user.pw_gecos.split(",", 1)[0]) + newuser['sn'] = ask("Your surname (only used for sorting entries)", + get_surname(newuser['cn'])) + account = ask("Your sourceware account", user.pw_name) + while True: + e = ask("Primary email address", None) + if email_valid(e): + break + print ("That address does not look valid.") + newuser['email'].append(e) + while (e := ask("Additional email (return to stop)", None, + required=False)): + if email_valid(e): + newuser['email'].append(e) + else: + print ("That address does not look valid. Ignored.") + gcc_email = account + "@gcc.gnu.org" + if not gcc_email in newuser['email']: + print(f"Appending {gcc_email} to the list of email addresses") + newuser['email'].append(gcc_email) + newuser['roles'] = list() + newuser['roles'].append('WriteAfter') + newuser['account'] = account + print("If you are using a Developer Certificate of Origin (DCO)") + print("you can add appropriate email addresses here") + while (e := ask("DCO email (return to stop)", None, required=False)): + if email_valid(e): + newuser['roles'].append({'DCO': e}) + else: + print ("That address does not look valid. Ignored.") + return newuser + +def main(): + optp = OptionParser(usage="Usage: %prog [] ") + optp.add_option("-o", "--output", metavar="FILE", + default=None, dest="outfilename", + help="Write to FILE instead of updating original file") + opts, args = optp.parse_args() + if len(args) != 1: + optp.print_help() + sys.exit(1) + + data = maintutils.load(args[0]) + # Check the data before we try to update it. + maintutils.validate(data) + newuser = getuserdata() + if not newuser: + return 0 + existing = [u for u in filter(lambda x: (x.get('account') + == newuser['account']), + data['users'])] + if len(existing): + print(f"Sorry that account name ({newuser['account']}) has already been used.") + print("Note, this script can only be used to add new accounts.") + return 1 + data['users'].append (newuser) + data['users'] = sorted(data['users'], + key = lambda k: (unilower(k['sn']), + unilower(k['cn']))) + if opts.outfilename and opts.outfilename != '-': + outfd = open (opts.outfilename, "w", encoding="utf-8") + elif opts.outfilename and opts.outfilename == '-': + outfd = sys.stdout + else: + outfd = open (args[0], "w", encoding="utf-8") + yaml.dump (data, outfd, allow_unicode = True, sort_keys = False) + return 0 + +if __name__ == "__main__": + sys.exit (main())