get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.1/patches/2229543/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2229543,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2229543/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/20260428130732.2479544-1-rearnsha@arm.com/",
    "project": {
        "id": 17,
        "url": "http://patchwork.ozlabs.org/api/1.1/projects/17/?format=api",
        "name": "GNU Compiler Collection",
        "link_name": "gcc",
        "list_id": "gcc-patches.gcc.gnu.org",
        "list_email": "gcc-patches@gcc.gnu.org",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null
    },
    "msgid": "<20260428130732.2479544-1-rearnsha@arm.com>",
    "date": "2026-04-28T13:07:32",
    "name": "[RFD] Forge: Add scaffolding for managing forge labels",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "b4fe8900cdef3686e52ac61e15f6871a67a4afee",
    "submitter": {
        "id": 4378,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/4378/?format=api",
        "name": "Richard Earnshaw",
        "email": "rearnsha@arm.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/20260428130732.2479544-1-rearnsha@arm.com/mbox/",
    "series": [
        {
            "id": 501848,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/501848/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=501848",
            "date": "2026-04-28T13:07:32",
            "name": "[RFD] Forge: Add scaffolding for managing forge labels",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/501848/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2229543/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2229543/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "gcc-patches@gcc.gnu.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@legolas.ozlabs.org",
            "gcc-patches@gcc.gnu.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=arm.com header.i=@arm.com header.a=rsa-sha256\n header.s=foss header.b=JmIQiAnw;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)",
            "sourceware.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key,\n unprotected) header.d=arm.com header.i=@arm.com header.a=rsa-sha256\n header.s=foss header.b=JmIQiAnw",
            "sourceware.org;\n dmarc=pass (p=none dis=none) header.from=arm.com",
            "sourceware.org; spf=pass smtp.mailfrom=arm.com",
            "server2.sourceware.org;\n arc=none smtp.remote-ip=217.140.110.172"
        ],
        "Received": [
            "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g4gjY6Y7sz1xvV\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 28 Apr 2026 23:08:32 +1000 (AEST)",
            "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 7EE064BB8F5E\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 28 Apr 2026 13:08:30 +0000 (GMT)",
            "from foss.arm.com (foss.arm.com [217.140.110.172])\n by sourceware.org (Postfix) with ESMTP id 477424BA2E38;\n Tue, 28 Apr 2026 13:07:51 +0000 (GMT)",
            "from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14])\n by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 38F2B2BCB;\n Tue, 28 Apr 2026 06:07:45 -0700 (PDT)",
            "from e142624-vm1.arm.com (e142624-vm1.arm.com [10.2.81.32])\n by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id EC8243F62B;\n Tue, 28 Apr 2026 06:07:49 -0700 (PDT)"
        ],
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 sourceware.org 7EE064BB8F5E",
            "OpenDKIM Filter v2.11.0 sourceware.org 477424BA2E38"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 477424BA2E38",
        "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 477424BA2E38",
        "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777381671; cv=none;\n b=pNj56ZOnsOmwuwPxGvC7qjH7AgOBR8TDryTZPGTL6tmYPE3iYBzF+jGl0BtxBQWGFmh3NeA5j0RV2OSHCvqHYv9e+BsIfygUkprwD6wCWqt0R1Y7dko7mbTXflSMkBWSSTIM2watrCxt3WQ/+1Ueksog+YdwhikdtCCs5/w3Zw0=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1777381671; c=relaxed/simple;\n bh=Ou32C9am+uM6HoLmkNPa06apt1U/tK4Di0Qr2g/2u3o=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=q69o76L84kb6DaE0BaSHhuDpBKSsg255lxkJ/poEvavrmL8J4KltF/xKe7kfc25neKQaTZ4CzaURS5Q7BAuBUKQ2vERTtEkuVIf1cStg+hG5RrooqkJid8xiEPper2slmZPIUgi0wxFnxD1P3aslQLov26OiuEXI3mRPnRB7mhA=",
        "ARC-Authentication-Results": "i=1; server2.sourceware.org",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss;\n t=1777381670; bh=Ou32C9am+uM6HoLmkNPa06apt1U/tK4Di0Qr2g/2u3o=;\n h=From:To:Cc:Subject:Date:From;\n b=JmIQiAnw4qhZdZurGgfV0CmFS84Nx6Qn0lZlG7hXWAIJ79kqUey4Tbm8oYZwzI7nG\n vbRgoAMzW8Rc55EXTqKODyhWHKtCEfLnCOCvEGAPYGlAzNul99uHYJpXX9hnKXtCdT\n ggK1p6tPfwtGurF2JMST4QeFpUmChaA00SYgjM1g=",
        "From": "Richard Earnshaw <rearnsha@arm.com>",
        "To": "gcc-patches@gcc.gnu.org",
        "Cc": "Richard Earnshaw <rearnsha@arm.com>,\n\tgcc@gcc.gnu.org",
        "Subject": "[RFD] Forge: Add scaffolding for managing forge labels",
        "Date": "Tue, 28 Apr 2026 14:07:32 +0100",
        "Message-ID": "<20260428130732.2479544-1-rearnsha@arm.com>",
        "X-Mailer": "git-send-email 2.43.0",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "gcc-patches@gcc.gnu.org",
        "X-Mailman-Version": "2.1.30",
        "Precedence": "list",
        "List-Id": "Gcc-patches mailing list <gcc-patches.gcc.gnu.org>",
        "List-Unsubscribe": "<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>",
        "List-Archive": "<https://gcc.gnu.org/pipermail/gcc-patches/>",
        "List-Post": "<mailto:gcc-patches@gcc.gnu.org>",
        "List-Help": "<mailto:gcc-patches-request@gcc.gnu.org?subject=help>",
        "List-Subscribe": "<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>",
        "Errors-To": "gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org"
    },
    "content": "Add some scaffolding for managing labels used by the forge.  This\npatch handles some general infrastructure to read a 'database' of\nlabels, an initial set of lables and a script to manage the list of\nlabels on the forge instance.  It makes use of the forge's REST API to\nautomate the process, thus making it simple for a user with\nappropriate privilages to update the list with minimal effort.\n\nThe classify.py script takes a list of file paths and applies the\nmatch rules, printing out the labels that would apply to each filename.\nYou can see the effects by running\n  (cd $GCC_SRC_BASE; find . -type f -print) | ./classify.py\n\nI expect a future patch to use the matching data to automatically\nlabel merge requests with the relevant labels as part of the initial\ntriaging process.\n\ncontrib/ChangeLog:\n\n\t* forge/forgelabels.py: New file.\n\t* forge/labels.yaml: New file.\n\t* forge/update-labels.py: New file.\n\t* forge/classify.py: New file.\n---\n\nThis is a rework of the RFD that I originally posted last year:\nhttps://gcc.gnu.org/pipermail/gcc/2025-October/246778.html\n\nSince then I've rewritten the labels data in Yaml to give it better structure.\nI did consider JSON, but rejected it on the grounds that it was a) more\nverbose and b) lacks proper support for comments.\n\nThere are still some things to be decided; for example, the script to\nupdate the forge puts all the labels on the repository (currently\ngcc-test), rather than at organisation (gcc) level.  That means that\nif we have more than one instance of the repository in the forge we\nwould need to duplicate any relevant labels; but that's probably the\nright thing for this label set since, for example, the Algo68 copy of\nthe repo would not really need this set of labels.\n\nI still need to add the 'docs' label, but this isn't the final version\nof this code, so...\n\nR.\n\n contrib/forge/classify.py      |  88 ++++\n contrib/forge/forgelabels.py   | 201 +++++++\n contrib/forge/labels.yaml      | 926 +++++++++++++++++++++++++++++++++\n contrib/forge/update-labels.py | 151 ++++++\n 4 files changed, 1366 insertions(+)\n create mode 100755 contrib/forge/classify.py\n create mode 100755 contrib/forge/forgelabels.py\n create mode 100644 contrib/forge/labels.yaml\n create mode 100755 contrib/forge/update-labels.py",
    "diff": "diff --git a/contrib/forge/classify.py b/contrib/forge/classify.py\nnew file mode 100755\nindex 00000000000..da77c9c7239\n--- /dev/null\n+++ b/contrib/forge/classify.py\n@@ -0,0 +1,88 @@\n+#!/usr/bin/env python3\n+\n+# Map files in the GCC source tree to forge labels.\n+\n+# Copyright (C) 2026 Free Software Foundation, Inc.\n+#\n+# This file is part of GCC.\n+#\n+# GCC is free software; you can redistribute it and/or modify it under\n+# the terms of the GNU General Public License as published by the Free\n+# Software Foundation; either version 3, or (at your option) any later\n+# version.\n+#\n+# GCC is distributed in the hope that it will be useful, but WITHOUT ANY\n+# WARRANTY; without even the implied warranty of MERCHANTABILITY or\n+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n+# for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with GCC; see the file COPYING3.  If not see\n+# <http://www.gnu.org/licenses/>.\n+\n+# Note, this file requires python 3.10 or later.\n+\n+import re\n+import sys\n+import forgelabels\n+\n+labels_file = 'labels'\n+\n+class classifier:\n+    def __init__ (self, groups):\n+        self.file_class = []\n+        self.bz_class = []\n+        for g in groups:\n+            if not g.match_defaults or g.match_defaults.mclass != \"file\":\n+                continue\n+            for l in g.labels:\n+                if l.match:\n+                    self.file_class.append (self._file_entry (l.match,\n+                                                              l.full_name))\n+\n+    def map_to_labels (self, name):\n+        labels = []\n+        priority = 100\n+        for fc in self.file_class:\n+            if fc['pattern'].match (name):\n+                if fc['priority'] < priority:\n+                    priority = fc['priority']\n+                    labels = [fc['label']]\n+                elif fc['priority'] == priority:\n+                    labels.append (fc['label'])\n+\n+        return labels\n+\n+    def _file_entry (self, match, label):\n+        priority = match.priority\n+\n+        d = dict()\n+        try:\n+            d['pattern'] = re.compile (match.file2re ())\n+        except Exception as e:\n+            # Re-raise the exception with some additional context\n+            raise Exception (f\"{label}: {str (e)}\")\n+\n+        d['priority'] = priority\n+        d['label'] = label\n+        return d\n+\n+def load ():\n+    \"\"\"\n+    Main entry point to initialize the classifier\n+    \"\"\"\n+    return classifier (forgelabels.load ())\n+\n+def main ():\n+    \"\"\"\n+    Entry point for testing the classifier.\n+    Use it with something like:\n+       (cd srcroot; find . -type f -print) | ./classify.py\n+    \"\"\"\n+    my_classifier = load ()\n+    for f in sys.stdin.readlines ():\n+        labels = my_classifier.map_to_labels (f[1:].strip ('\\n'))\n+        print (f\"{f.strip ('\\n')}: {labels}\")\n+\n+if __name__ == \"__main__\":\n+    main ()\ndiff --git a/contrib/forge/forgelabels.py b/contrib/forge/forgelabels.py\nnew file mode 100755\nindex 00000000000..b4b42ba0feb\n--- /dev/null\n+++ b/contrib/forge/forgelabels.py\n@@ -0,0 +1,201 @@\n+#! /usr/bin/env python3\n+\n+# Read the list of label data for a forgejo instance of GCC\n+\n+# Copyright (C) 2026 Free Software Foundation, Inc.\n+#\n+# This file is part of GCC.\n+#\n+# GCC is free software; you can redistribute it and/or modify it under\n+# the terms of the GNU General Public License as published by the Free\n+# Software Foundation; either version 3, or (at your option) any later\n+# version.\n+#\n+# GCC is distributed in the hope that it will be useful, but WITHOUT ANY\n+# WARRANTY; without even the implied warranty of MERCHANTABILITY or\n+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n+# for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with GCC; see the file COPYING3.  If not see\n+# <http://www.gnu.org/licenses/>.\n+\n+# Variables needed by the script.  Can be overridden by setting the\n+# same name in the environment\n+\n+import yaml\n+\n+# Prefer the libYAML implementations if available.  Fall back to the\n+# pure python versions as a backup.\n+try:\n+    from yaml import CLoader as Loader, CDumper as Dumper\n+except:\n+    from yaml import Loader, Dumper\n+\n+labels_file = \"labels.yaml\"\n+\n+class match_defaults:\n+    def __init__(self, md_data):\n+        self.mclass = 'file'\n+        if 'class' in md_data:\n+            if (md_data['class'] == \"file\"\n+                or md_data['class'] == \"BZ\"):\n+                self.mclass = md_data['class']\n+            else:\n+                raise Exception (\"Unsupported match class: {md_data['class']}\")\n+\n+        # todo: Add BZ class support\n+\n+class label_match:\n+    def __init__(self, lm_data):\n+        self.file = None\n+        self.priority = 1\n+        if 'file' in lm_data:\n+            self.file = lm_data['file']\n+        if 'priority' in lm_data:\n+            self.priority = lm_data['priority']\n+        # todo: Add BZ match support\n+\n+    def file2re(self):\n+        \"\"\"\n+        Convert the extended glob file entry to a standard regular expression.\n+\n+        Supported features:\n+        ^/ at the start of an entry anchors to the root of the file tree.\n+        \\<char> passes <char> through to the RE compiler, without any of\n+           the interpretations below.\n+        /**/ matches an arbitrary depth of directories.\n+        * matches any character except '/'.\n+        + matches the literal '+'.\n+        . matches the literal '.'.\n+        (...|...[|...]+) match any of the alternatives.\n+        \"\"\"\n+        if not self.file:\n+            raise Exception (\"Match rule lacks a 'file' entry\")\n+        template = self.file\n+        pos = 0\n+        depth = 0\n+        last_char = None\n+        pattern = r''\n+        length = len (template)\n+        if template[pos] == '^':\n+            pattern += r'^'\n+            pos += 1\n+            # Don't update last char\n+        else:\n+            pattern += r'.*/'\n+\n+        while pos < length:\n+            if template[pos] == '\\\\' and pos + 1 < length:\n+                pos += 1\n+                pattern += template[pos]\n+            # '*' is mapped to '[^/]*' unless the following characters are\n+            # also '*/', in which case we match '(.*/)*'\n+            elif template[pos] == '*':\n+                if pos + 2 < length and template[:3] == '**/':\n+                    pattern += r'(:?.*/)*'\n+                    pos += 1\n+                else:\n+                    pattern += r'[^/]*'\n+            elif template[pos] == '+':\n+                pattern += r'\\+'\n+            elif template[pos] == '(':\n+                # We don't need any groups from the regex.\n+                pattern += r'(?:'\n+                depth += 1\n+            elif template[pos] == ')':\n+                pattern += r')'\n+                depth -= 1\n+                if depth < 0:\n+                    raise Exception ('Invalid template: ' + template)\n+            elif template[pos] == '.':\n+                pattern += r'\\.'\n+            elif template[pos] == '/':\n+                pattern += r'/'\n+                if (depth > 0\n+                    and pos + 1 < length \n+                    and (template[pos + 1] == '|'\n+                         or template[pos + 1] == ')')):\n+                    # We expect '/|' (or '/)' to appear at the end of\n+                    # an alternative and to mean anything in any\n+                    # subdirectory matched.  But we may need to match\n+                    # files in the alternative, so add an explicit\n+                    # '.*'.\n+                    pattern += r'.*'\n+            else:\n+                pattern += template[pos]\n+            last_char = template[pos]\n+            pos += 1\n+\n+        if depth != 0:\n+            raise Exception ('Invalid template: ' + template)\n+        if last_char != '/':\n+            pattern += '$'\n+\n+        return pattern\n+\n+class label:\n+    def __init__(self, l_data, group):\n+        self.name = None\n+        self.full_name = None\n+        self.desc = None\n+        self.match = None\n+        self.color_val = None\n+        self.group = group\n+        if 'name' in l_data:\n+            self.name = l_data['name']\n+            self.full_name = group.group + '/' + self.name\n+        else:\n+            raise Exception (f\"Missing label name (in group '{group.group}').\")\n+        if 'description' in l_data:\n+            self.desc = l_data['description']\n+        if 'match' in l_data:\n+            self.match = label_match(l_data['match'])\n+    def color(self):\n+        if self.color_val:\n+            return self.color_val\n+        return self.group.color\n+\n+class group:\n+    def __init__(self, gr_data):\n+        # defaults\n+        self.exclusive = False\n+        self.color = \"#ff0000\"  # Red\n+        self.match_defaults = None\n+        self.labels = []\n+\n+        if 'group' in gr_data:\n+            self.group = gr_data['group']\n+        else:\n+            raise Exception ('All groups must specify their name')\n+        if 'color' in gr_data:\n+            self.color = gr_data['color']\n+        if 'exclusive' in gr_data:\n+            self.exclusive = gr_data['exclusive']\n+        if 'match_defaults' in gr_data:\n+            self.match_defaults = match_defaults(gr_data['match_defaults'])\n+        if 'labels' in gr_data:\n+            for l in gr_data['labels']:\n+                self.labels.append(label(l, self))\n+        else:\n+            raise Exception (f\"Group {self.group} must contain at least one label\")\n+\n+def load():\n+    with open(labels_file) as f:\n+        groups = []\n+        data = yaml.load (f, Loader=Loader)\n+\n+        try:\n+            for g in data['label_data']:\n+                groups.append(group(g))\n+\n+        except Exception as e:\n+            raise e\n+            return\n+    return groups\n+\n+def main():\n+    print (load())\n+\n+if __name__ == \"__main__\":\n+    main()\ndiff --git a/contrib/forge/labels.yaml b/contrib/forge/labels.yaml\nnew file mode 100644\nindex 00000000000..a0627c0d5c7\n--- /dev/null\n+++ b/contrib/forge/labels.yaml\n@@ -0,0 +1,926 @@\n+# Provisional list of labels for the gcc forge:\n+#\n+# Copyright (C) 2026 Free Software Foundation, Inc.\n+#\n+# This file is part of GCC.\n+#\n+# GCC is free software; you can redistribute it and/or modify it under\n+# the terms of the GNU General Public License as published by the Free\n+# Software Foundation; either version 3, or (at your option) any later\n+# version.\n+#\n+# GCC is distributed in the hope that it will be useful, but WITHOUT ANY\n+# WARRANTY; without even the implied warranty of MERCHANTABILITY or\n+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n+# for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with GCC; see the file COPYING3.  If not see\n+# <http://www.gnu.org/licenses/>.\n+\n+# Tags are not exclusive unless otherwise stated.\n+\n+# Note, *** indicates that this matches a strict category, but perhaps\n+# we shouldn't permit these in labels.\n+\n+# Format of this file:\n+#\n+#   We currently recognize three classes matching rule for auto-applying\n+#   labels: BZ, file and none.\n+#   The matching rules are inherrited up the data scopes with the inner\n+#   most rule taking precedence.  If no rule can be found, then the\n+#   default rule of 'none' is applied.\n+#   - BZ\n+#     If the patch cover letter references a Bugzilla ticket, the ticket\n+#     number will be looked up in the Bugzilla database and the\n+#     appropriate data used to apply the relevant label.\n+#   - file\n+#     The extended glob patterns are matched against the files modified\n+#     and the label is attached if the pattern matches and no higher\n+#     priority matches exist.  The default priority for a match is 1\n+#   - none\n+#     Can be used to disable a label from being selected by matching\n+#     when a default class would otherwise be inherrited.\n+\n+\n+label_data: \n+  # Bug reports\n+  # All of these labels are expected to be automatically scraped from\n+  # the primary (first-referenced) bugzilla entry that is identified\n+  # from the cover text of a patch.  Note that they will not be\n+  # automatically updated if the bugzilla entry is changed after the\n+  # pull request has been created.\n+  - group: Bug\n+    # Labels relating to bugs with a 'regression' marker in the\n+    # bug summary.\n+    exclusive: false\n+    color: \"bd0000\"\n+    labels:\n+      - name: Regression\n+        description: \"The referenced Bugzilla ticket is a regression\"\n+        match:\n+          class: BZ\n+          field: summary\n+          value: \"imatch(\\\\[[^\\\\]]*regression\\\\])\"\n+  - group: Bug/Importance\n+    exclusive: true\n+    color: \"f06d00\"\n+    match_defaults:\n+      class: BZ\n+      field: importance\n+    labels:\n+      - name: \"P1\"\n+        description: \"The Bugzilla ticket has priority P1\"\n+        match:\n+          value: \"== 'P1'\"\n+      - name: \"P2\"\n+        description: \"The Bugzilla ticket has priority P2\"\n+        match:\n+          value: \"== 'P2'\"\n+      - name: \"P3\"\n+        description: \"The Bugzilla ticket has priority P3\"\n+        match:\n+          value: \"== 'P3'\"\n+      - name: \"P4\"\n+        description: \"The Bugzilla ticket has priority P4\"\n+        match:\n+          value: \"== 'P4'\"\n+      - name: \"P5\"\n+        description: \"The Bugzilla ticket has priority P5\"\n+        match:\n+          value: \"== 'P5'\"\n+  # Labels relating to bugs with a specific component.  The final\n+  # part of the label should be a string match for the component\n+  # field of the BZ entry.\n+  - group: \"Bug/Component\"\n+    exclusive: true\n+    color: \"fbca04\"\n+    match_defaults:\n+      class: BZ\n+      field: component\n+      label_name: true\n+    labels:\n+      - name: \"ada\"\n+        description: \"The Bugzilla component is 'ada'\"\n+      - name: \"algol68\"\n+        description: \"The Bugzilla component is 'algol68'\"\n+      - name: \"analyzer\"\n+        description: \"The Bugzilla component is 'analyzer'\"\n+      - name: \"boehm-gc\"\n+        description: \"The Bugzilla component is 'boehm-gc'\"\n+      - name: \"bootstrap\"\n+        description: \"The Bugzilla component is 'bootstrap'\"\n+      - name: \"c\"\n+        description: \"The Bugzilla component is 'c'\"\n+      - name: \"c++\"\n+        description: \"The Bugzilla component is 'c++'\"\n+      - name: \"cobol\"\n+        description: \"The Bugzilla component is 'cobol'\"\n+      - name: \"d\"\n+        description: \"The Bugzilla component is 'd'\"\n+      - name: \"debug\"\n+        description: \"The Bugzilla component is 'debug'\"\n+      - name: \"demangler\"\n+        description: \"The Bugzilla component is 'demangler'\"\n+      - name: \"diagnostics\"\n+        description: \"The Bugzilla component is 'diagnostics'\"\n+      - name: \"driver\"\n+        description: \"The Bugzilla component is 'driver'\"\n+      - name: \"fortran\"\n+        description: \"The Bugzilla component is 'fortran'\"\n+      - name: \"gcov-profile\"\n+        description: \"The Bugzilla component is 'gcov-profile'\"\n+      - name: \"go\"\n+        description: \"The Bugzilla component is 'go'\"\n+      - name: \"ipa\"\n+        description: \"The Bugzilla component is 'ipa'\"\n+      - name: \"jit\"\n+        description: \"The Bugzilla component is 'jit'\"\n+      - name: \"libbacktrace\"\n+        description: \"The Bugzilla component is 'libbacktrace'\"\n+      - name: \"libcc1\"\n+        description: \"The Bugzilla component is 'libcc1'\"\n+      - name: \"libffi\"\n+        description: \"The Bugzilla component is 'libffi'\"\n+      - name: \"libfortran\"\n+        description: \"The Bugzilla component is 'libfortran'\"\n+      - name: \"libgcc\"\n+        description: \"The Bugzilla component is 'libgcc'\"\n+      - name: \"libgdiagnostics\"\n+        description: \"The Bugzilla component is 'libdiagnostics'\"\n+      - name: \"libgomp\"\n+        description: \"The Bugzilla component is 'libgomp'\"\n+      - name: \"libitm\"\n+        description: \"The Bugzilla component is 'libitm'\"\n+      - name: \"libobjc\"\n+        description: \"The Bugzilla component is 'libobjc'\"\n+      - name: \"libquadmath\"\n+        description: \"The Bugzilla component is 'libquadmath'\"\n+      - name: \"libstdc++\"\n+        description: \"The Bugzilla component is 'libstdc++'\"\n+      - name: \"lto\"\n+        description: \"The Bugzilla component is 'lto'\"\n+      - name: \"middle-end\"\n+        description: \"The Bugzilla component is 'middle-end'\"\n+      - name: \"modula2\"\n+        description: \"The Bugzilla component is 'modula2'\"\n+      - name: \"objc\"\n+        description: \"The Bugzilla component is 'objc'\"\n+      - name: \"other\"\n+        description: \"The Bugzilla component is 'other'\"\n+      - name: \"pch\"\n+        description: \"The Bugzilla component is 'pch'\"\n+      - name: \"pending\"\n+        description: \"The Bugzilla component is 'pending'\"\n+      - name: \"plugins\"\n+        description: \"The Bugzilla component is 'plugins'\"\n+      - name: \"preprocessor\"\n+        description: \"The Bugzilla component is 'preprocessor'\"\n+      - name: \"regression\"\n+        description: \"The Bugzilla component is 'regression'\"\n+      - name: \"rtl-optimization\"\n+        description: \"The Bugzilla component is 'rtl-optimization'\"\n+      - name: \"rust\"\n+        description: \"The Bugzilla component is 'rust'\"\n+      - name: \"sanitizer\"\n+        description: \"The Bugzilla component is 'sanitizer'\"\n+      - name: \"sarif-replay\"\n+        description: \"The Bugzilla component is 'sarif-replay'\"\n+      - name: \"target\"\n+        description: \"The Bugzilla component is 'target'\"\n+      - name: \"testsuite\"\n+        description: \"The Bugzilla component is 'testsuite'\"\n+      - name: \"translation\"\n+        description: \"The Bugzilla component is 'translation'\"\n+      - name: \"tree-optimization\"\n+        description: \"The Bugzilla component is 'tree-optimization'\"\n+      - name: \"web\"\n+        description: \"The Bugzilla component is 'web'\"\n+  # Labels relating to releases (for backporting)\n+  - group: \"Release\"\n+    exclusive: false\n+    color: \"5319e7\"\n+    labels:\n+      - name: \"GCC-13\"\n+        description: \"Consider backporting to GCC-13\"\n+      - name: \"GCC-14\"\n+        description: \"Consider backporting to GCC-14\"\n+      - name: \"GCC-15\"\n+        description: \"Consider backporting to GCC-15\"\n+      - name: \"GCC-16\"\n+        description: \"Consider backporting to GCC-16\"\n+  # Labels related to components\n+  # Note that multiple labels in this section can be applied, based on\n+  # the files modified.\n+  - group: \"General\"\n+    exclusive: false\n+    color: \"006b75\"\n+    match_defaults:\n+      class: \"file\"\n+    labels:\n+      - name: \"config\"\n+        description: \"Affects configure or autoconf scripts\"\n+        match:\n+          file: \"^/(config/|**/*.(in|ac|m4)|**/configure*)\"\n+          priority: 2\n+      - name: \"contrib\"\n+        description: \"Miscellaneous support scripts\"\n+        match:\n+          file: \"^/contrib/\"\n+      - name: \"make\"\n+        description: \"Affects Makefiles or automake\"\n+        match:\n+          file: \"(Makefile(.*)?|*.am)\"\n+          priority: 2\n+      - name: \"driver\"\n+        description: \"GCC main driver program\"\n+        match:\n+          file: \"^/gcc/gcc(-main|-ar)?.(cc|h)\"\n+      - name: \"forge\"\n+        description: \"Forge and CI infrastructure\"\n+        match:\n+          file: \"^/.(forgejo|github)/\"\n+      - name: \"unknown\"\n+        description: \"Catch-all, does not match any other category\"\n+        match:\n+          file: \"*\"\n+          priority: 10\n+      - name: \"gdbhooks\"\n+        description: \"Support for debugging GCC with GDB (gdbhooks.py)\"\n+        match:\n+          file: \"^/gcc/gdb(hooks.py|init.in)\"\n+      - name: \"docs\"\n+        description: \"Changes to the documentation\"\n+        match:\n+          file: \"docs?/\"\n+      - name: \"web\"\n+        description: \"Changes to the web pages\"\n+        # No match entry!\n+\n+  # Library components\n+  - group: \"Library\"\n+    exclusive: false\n+    color: \"009800\"\n+    match_defaults:\n+      class: \"file\"\n+    labels:\n+      - name: \"libada\"\n+        description: \"Affects libada\"\n+        match:\n+          file: \"^/libada/\"\n+      - name: \"libatomic\"\n+        description: \"Affects libatomic\"\n+        match:\n+          file: \"^/libatomic/\"\n+      - name: \"libbacktrace\"\n+        description: \"Affects libbacktrace\"\n+        match:\n+          file: \"^/libbacktrace/\"\n+      - name: \"libcc1\"\n+        description: \"Affects libcc1\"\n+        match:\n+          file: \"^/libcc1/\"\n+      - name: \"libcody\"\n+        description: \"Affects libcody\"\n+        match:\n+          file: \"^/libcody/\"\n+      - name: \"libcpp\"\n+        description: \"Affects libcpp\"\n+        match:\n+          file: \"^/libcpp/\"\n+      - name: \"libdecnumber\"\n+        description: \"Affects libdecnumber\"\n+        match:\n+          file: \"^/libdecnumber/\"\n+      - name: \"libffi\"\n+        description: \"Affects libffi\"\n+        match:\n+          file: \"^/libffi/\"\n+      - name: \"libgcc\"\n+        description: \"Affects libgcc\"\n+        match:\n+          file: \"^/libgcc/\"\n+      - name: \"libgcobol\"\n+        description: \"Affects libgcobol\"\n+        match:\n+          file: \"^/libgcobol/\"\n+      - name: \"libgfortran\"\n+        description: \"Affects libgfortran\"\n+        match:\n+          file: \"^/libgfortran/\"\n+      - name: \"libgm2\"\n+        description: \"Affects libgm2\"\n+        match:\n+          file: \"^/libgm2/\"\n+      - name: \"libgo\"\n+        description: \"Affects libgo\"\n+        match:\n+          file: \"^/libgo/\"\n+      - name: \"libgomp\"\n+        description: \"Affects libgomp\"\n+        match:\n+          file: \"^/libgomp/\"\n+      - name: \"libgrust\"\n+        description: \"Affects libgrust\"\n+        match:\n+          file: \"^/libgrust/\"\n+      - name: \"libiberty\"\n+        description: \"Affects libiberty\"\n+        match:\n+          file: \"^/(include|libiberty)/\"\n+      - name: \"libitm\"\n+        description: \"Affects libitm\"\n+        match:\n+          file: \"^/libitm/\"\n+      - name: \"libobjc\"\n+        description: \"Affects libobjc\"\n+        match:\n+          file: \"^/libobjc/\"\n+      - name: \"libphobos\"\n+        description: \"Affects libphobos\"\n+        match:\n+          file: \"^/libphobos/\"\n+      - name: \"libquadmath\"\n+        description: \"Affects libquadmath\"\n+        match:\n+          file: \"^/libquadmath/\"\n+      - name: \"libsanitizer\"\n+        description: \"Affects libsanitizer\"\n+        match:\n+          file: \"^/libsanitizer/\"\n+      - name: \"libssp\"\n+        description: \"Affects libssp\"\n+        match:\n+          file: \"^/libssp/\"\n+      - name: \"libstdc++\"\n+        description: \"Affects libstdc++\"\n+        match:\n+          file: \"^/libstdc++-v3/\"\n+      - name: \"libvtv\"\n+        description: \"Affects libvtv\"\n+        match:\n+          file: \"^/libvtv/\"\n+      - name: \"zlib\"\n+        description: \"Affects zlib\"\n+        match:\n+          file: \"^/zlib/\"\n+  # CPU targets (in compiler or in libraries)\n+  - group: \"Target\"\n+    exclusive: false\n+    color: \"70c24a\"\n+    match_defaults:\n+      class: \"file\"\n+    labels:\n+      - name: \"aarch64\"\n+        description: \"Affects the aarch64 target\"\n+        match:\n+          file: \"config/aarch64/\"\n+      - name: \"alpha\"\n+        description: \"Affects the alpha target\"\n+        match:\n+          file: \"config/alpha/\"\n+      - name: \"arc\"\n+        description: \"Affects the arc target\"\n+        match:\n+          file: \"config/arc/\"\n+      - name: \"arm\"\n+        description: \"Affects the arm target\"\n+        match:\n+          file: \"config/arm/\"\n+      - name: \"avr\"\n+        description: \"Affects the avr target\"\n+        match:\n+          file: \"config/avr/\"\n+      - name: \"bfin\"\n+        description: \"Affects the bfin target\"\n+        match:\n+          file: \"config/bfin/\"\n+      - name: \"bpf\"\n+        description: \"Affects the bpf target\"\n+        match:\n+          file: \"config/bpf/\"\n+      - name: \"c6x\"\n+        description: \"Affects the c6x target\"\n+        match:\n+          file: \"config/c6x/\"\n+      - name: \"cris\"\n+        description: \"Affects the cris target\"\n+        match:\n+          file: \"config/cris/\"\n+      - name: \"csky\"\n+        description: \"Affects the csky target\"\n+        match:\n+          file: \"config/csky/\"\n+      - name: \"epiphany\"\n+        description: \"Affects the epiphany target\"\n+        match:\n+          file: \"config/epiphany/\"\n+      - name: \"fr30\"\n+        description: \"Affects the fr30 target\"\n+        match:\n+          file: \"config/fr30/\"\n+      - name: \"frv\"\n+        description: \"Affects the frv target\"\n+        match:\n+          file: \"config/frv/\"\n+      - name: \"ft32\"\n+        description: \"Affects the ft32 target\"\n+        match:\n+          file: \"config/ft32/\"\n+      - name: \"gcn\"\n+        description: \"Affects the gcn target\"\n+        match:\n+          file: \"config/gcn/\"\n+      - name: \"h8300\"\n+        description: \"Affects the h8300 target\"\n+        match:\n+          file: \"config/h8300/\"\n+      - name: \"i386\"\n+        description: \"Affects the i386 target\"\n+        match:\n+          file: \"config/i386/\"\n+      - name: \"ia64\"\n+        description: \"Affects the ia64 target\"\n+        match:\n+          file: \"config/ia64/\"\n+      - name: \"iq2000\"\n+        description: \"Affects the iq2000 target\"\n+        match:\n+          file: \"config/iq2000/\"\n+      - name: \"lm32\"\n+        description: \"Affects the lm32 target\"\n+        match:\n+          file: \"config/lm32/\"\n+      - name: \"loongarch\"\n+        description: \"Affects the loongarch target\"\n+        match:\n+          file: \"config/loongarch/\"\n+      - name: \"m32c\"\n+        description: \"Affects the m32c target\"\n+        match:\n+          file: \"config/m32c/\"\n+      - name: \"m32r\"\n+        description: \"Affects the m32r target\"\n+        match:\n+          file: \"config/m32r/\"\n+      - name: \"m68k\"\n+        description: \"Affects the m68k target\"\n+        match:\n+          file: \"config/m68k/\"\n+      - name: \"mcore\"\n+        description: \"Affects the mcore target\"\n+        match:\n+          file: \"config/mcore/\"\n+      - name: \"microblaze\"\n+        description: \"Affects the microblaze target\"\n+        match:\n+          file: \"config/microblaze/\"\n+      - name: \"mips\"\n+        description: \"Affects the mips target\"\n+        match:\n+          file: \"config/mips/\"\n+      - name: \"mmix\"\n+        description: \"Affects the mmix target\"\n+        match:\n+          file: \"config/mmix/\"\n+      - name: \"mn10300\"\n+        description: \"Affects the mn1030 target\"\n+        match:\n+          file: \"config/mn10300/\"\n+      - name: \"moxie\"\n+        description: \"Affects the moxie target\"\n+        match:\n+          file: \"config/moxie/\"\n+      - name: \"msp430\"\n+        description: \"Affects the msp430 target\"\n+        match:\n+          file: \"config/msp430/\"\n+      - name: \"nds32\"\n+        description: \"Affects the nds32 target\"\n+        match:\n+          file: \"config/nds32/\"\n+      - name: \"nvptx\"\n+        description: \"Affects the nvptx target\"\n+        match:\n+          file: \"config/nvptx/\"\n+      - name: \"or1k\"\n+        description: \"Affects the or1k target\"\n+        match:\n+          file: \"config/or1k/\"\n+      - name: \"pa\"\n+        description: \"Affects the pa target\"\n+        match:\n+          file: \"config/pa/\"\n+      - name: \"pdp11\"\n+        description: \"Affects the pdp1 target\"\n+        match:\n+          file: \"config/pdp11/\"\n+      - name: \"pru\"\n+        description: \"Affects the pru target\"\n+        match:\n+          file: \"config/pru/\"\n+      - name: \"riscv\"\n+        description: \"Affects the riscv target\"\n+        match:\n+          file: \"config/riscv/\"\n+      - name: \"rl78\"\n+        description: \"Affects the rl78 target\"\n+        match:\n+          file: \"config/rl78/\"\n+      - name: \"rs6000\"\n+        description: \"Affects the rs6000 target\"\n+        match:\n+          file: \"config/rs6000/\"\n+      - name: \"rx\"\n+        description: \"Affects the rx target\"\n+        match:\n+          file: \"config/rx/\"\n+      - name: \"s390\"\n+        description: \"Affects the s390 target\"\n+        match:\n+          file: \"config/s390/\"\n+      - name: \"sh\"\n+        description: \"Affects the sh target\"\n+        match:\n+          file: \"config/sh/\"\n+      - name: \"sparc\"\n+        description: \"Affects the sparc target\"\n+        match:\n+          file: \"config/sparc/\"\n+      - name: \"stormy16\"\n+        description: \"Affects the stormy16 target\"\n+        match:\n+          file: \"config/x?stormy16/\"\n+      - name: \"v850\"\n+        description: \"Affects the v850 target\"\n+        match:\n+          file: \"config/v850/\"\n+      - name: \"vax\"\n+        description: \"Affects the vax target\"\n+        match:\n+          file: \"config/vax/\"\n+      - name: \"visium\"\n+        description: \"Affects the visium target\"\n+        match:\n+          file: \"config/visium/\"\n+      - name: \"xtensa\"\n+        description: \"Affects the xtensa target\"\n+        match:\n+          file: \"config/xtensa/\"\n+  # Labels specific to an object file format\n+  - group: \"Obj\"\n+    exclusive: false\n+    color: \"b0ffb0\"\n+    match_defaults:\n+      class: \"file\"\n+    labels:\n+      - name: \"coff\"\n+        description: \"Target independent code related to COFF object format\"\n+        match:\n+          file: \"*coff*\"\n+          priority: 2\n+      - name: \"elf\"\n+        description: \"Target independent code related to COFF object format\"\n+        match:\n+          file: \"*elf*\"\n+          priority: 2\n+      - name: \"pe\"\n+        description: \"Target independent code related to PE-COFF object format\"\n+        # No auto-match entry.\n+  # Labels relating to a specific OS\n+  - group: \"OS\"\n+    exclusive: false\n+    color: \"b0fff9\"\n+    match_defaults:\n+      class: \"file\"\n+    labels:\n+      - name: \"aix\"\n+        description: \"Affects AIX OS\"\n+        match:\n+          file: \"rs6000/*aix*\"\n+      - name: \"android\"\n+        description: \"Affects Android OS\"\n+        match:\n+          file: \"*android*\"\n+      - name: \"cygwin\"\n+        description: \"Affects cywin or mingw OSes\"\n+        match:\n+          file: \"(config/mingw/|*(cyqwin|mingw)*)\"\n+      - name: \"darwin\"\n+        description: \"Affects Darwin OS\"\n+        match:\n+          file: \"*darwin*\"\n+      - name: \"dragonfly\"\n+        description: \"Affects Dragonfly OS\"\n+        match:\n+          file: \"*dragonfly*\"\n+      - name: \"freebsd\"\n+        description: \"Affects FreeBSD OS\"\n+        match:\n+          file: \"*freebsd*\"\n+      - name: \"hpux\"\n+        description: \"Affects HP-UX OS\"\n+        match:\n+          file: \"*hpux*\"\n+      - name: \"hurd\"\n+        description: \"Affects GNU Hurd OS\"\n+        match:\n+          file: \"*hurd*\"\n+      - name: \"linux\"\n+        description: \"Affects generic Linux OS\"\n+        match:\n+          file: \"*linux*\"\n+          priority: 2\n+      - name: \"netbsd\"\n+        description: \"Affects NetBSD OS\"\n+        match:\n+          file: \"*netbsd*\"\n+      - name: \"openbsd\"\n+        description: \"Affects OpenBSD OS\"\n+        match:\n+          file: \"*openbsd*\"\n+      - name: \"rtems\"\n+        description: \"Affects RTEMS OS\"\n+        match:\n+          file: \"*rtems*\"\n+      - name: \"solaris\"\n+        description: \"Affects Solaris OS\"\n+        match:\n+          file: \"(sol2*|*solaris*)\"\n+      - name: \"vms\"\n+        description: \"Affects VMS OS\"\n+        match:\n+          file: \"(*[^a-z])?vms*\"\n+      - name: \"vxworks\"\n+        description: \"Affects VxWorks\"\n+        match:\n+          file: \"(config/vxworks/|*vxworks*)\"\n+      - name: \"windows\"\n+        description: \"Affects Windows OS\"\n+        match:\n+          file: \"*windows*\"\n+  # Labels relating to compiler mid-end\n+  - group: \"Midend\"\n+    exclusive: false\n+    color: \"207de5\"\n+    match_defaults:\n+      class: \"file\"\n+    labels:\n+      - name: \"diagnostics\"\n+        description: \"Diagnostics framework\"\n+        match:\n+          file: \"^/gcc/(diagnostics/|text-art/|*diagnostic*|*sarif*)\"\n+      - name: \"rtl\"\n+        description: \"General RTL code\"\n+        match:\n+          file: \"^/gcc/*.(cc|c|h)\"\n+          priority: 3\n+      - name: \"reg-alloc\"\n+        description: \"Register allocation code\"\n+        match:\n+          file: \"^/gcc/*(ira|lra)*\"\n+      - name: \"tree\"\n+        description: \"General tree/gimple code\"\n+        match:\n+          file: \"^/gcc/*tree*\"\n+          priority: 2\n+      - name: \"vect\"\n+        description: \"Vectorization\"\n+        match:\n+          file: \"^/gcc/*vect*\"\n+      - name: \"cfg\"\n+        description: \"Control Flow Graph framework\"\n+        match:\n+          file: \"^/gcc/*cfg*\"\n+      - name: \"ggc\"\n+        description: \"Garbage collection\"\n+        match:\n+          file: \"^/gcc/*ggc*\"\n+      - name: \"gen\"\n+        description: \"Generator programs, eg genattr\"\n+        match:\n+          file: \"^/gcc/gen*.(cc|h)\"\n+      - name: \"ipa\"\n+        description: \"Inter Procedural Analysis framework\"\n+        match:\n+          file: \"^/gcc/ipa*\"\n+      - name: \"misc\"\n+        description: \"Miscellaneous files that doesn't fit anything else\"\n+        match:\n+          file: \"^/gcc/*\"\n+          priority: 4\n+      - name: \"lto\"\n+        description: \"Link Time Optimizer code\"\n+        match:\n+          file: \"^/gcc/(lto/|*lto*)\"\n+      - name: \"jit\"\n+        description: \"JIT (Just-In-Time) code\"\n+        match:\n+          file: \"^/gcc/jit/\"\n+      - name: \"include\"\n+        description: \"Include headers and fixincludes\"\n+        match:\n+          file: \"^/(fixincludes|gcc/ginclude)/\"\n+      - name: \"dwarf\"\n+        description: \"Dwarf debug information\"\n+        match:\n+          file: \"^/gcc/**/*dwarf*\"\n+      - name: \"debug\"\n+        description: \"Non-dwarf debug support\"\n+        match:\n+          file: \"^/gcc/*ctf*\"\n+      - name: \"gimple\"\n+        description: \"General GIMPLE support\"\n+        match:\n+          file: \"^/gcc/*gimple*\"\n+      - name: \"gcse\"\n+        description: \"RTL GCSE\"\n+        match:\n+          file: \"^/gcc/*gcse*\"\n+      - name: \"jump\"\n+        description: \"Jump and branch optimizations\"\n+        match:\n+          file: \"^/gcc/*jump*\"\n+      - name: \"i18n\"\n+        description: \"Internationalization and Localization\"\n+        match:\n+          file: \"^/gcc/(po/|intl.*)\"\n+      - name: \"gcov\"\n+        description: \"GCOV code coverage support\"\n+        match:\n+          file: \"^/gcc/gcov*\"\n+      - name: \"opts\"\n+        description: \"Option framework\"\n+        match:\n+          file: \"^/gcc/(opt*.awk|*.opt|opt[-s]*)\"\n+      - name: \"ranger\"\n+        description: \"Value range framework\"\n+        match:\n+          file: \"^/gcc/*range*\"\n+      - name: \"rtl-ssa\"\n+        description: \"RTL SSA framework\"\n+        match:\n+          file: \"^/gcc/rtl-ssa/\"\n+      - name: \"tree-ssa\"\n+        description: \"Tree SSA framework\"\n+        match:\n+          file: \"^/gcc/(tree|gimple)-ssa*\"\n+      - name: \"autofdo\"\n+        description: \"AutoFDO framework\"\n+        match:\n+          file: \"^/gcc/auto-profile*\"\n+      - name: \"combine\"\n+        description: \"Instruction Combiner\"\n+        match:\n+          file: \"^/gcc/combine*\"\n+      - name: \"reload\"\n+        description: \"Legacy reload pass (obsolete)\"\n+        match:\n+          file: \"^/gcc/reload*\"\n+      - name: \"pair-fusion\"\n+        description: \"Pair Fusion framework\"\n+        match:\n+          file: \"^/gcc/pair-fusion*\"\n+      - name: \"ivopts\"\n+        description: \"Loop IVopt framework\"\n+        match:\n+          file: \"^/gcc/*loop-iv*\"\n+      - name: \"loop\"\n+        description: \"Loop optimizations\"\n+        match:\n+          file: \"^/gcc/*loop*\"\n+      - name: \"openacc\"\n+        description: \"OpenACC framework\"\n+        # No auto-match entry\n+      - name: \"openmp\"\n+        description: \"OpenMP framework\"\n+        match:\n+          file: \"^/gcc/omp*\"\n+      - name: \"analyzer\"\n+        description: \"Static analyzer\"\n+        match:\n+          file: \"^/gcc/analyzer/\"\n+  # Labels relating to language frontends\n+  - group: \"Frontend\"\n+    exclusive: false\n+    color: \"0052cc\"\n+    match_defaults:\n+      class: \"file\"\n+    labels:\n+      - name: \"ada\"\n+        description: \"The Ada frontend\"\n+        match:\n+          file: \"^/gcc/ada/\"\n+      - name: \"algol68\"\n+        description: \"The Algol68 frontend\"\n+        match:\n+          file: \"^/gcc/algol68/\"\n+      - name: \"c\"\n+        description: \"The C frontend\"\n+        match:\n+          file: \"^/gcc/c(|-family)/\"\n+      - name: \"c++\"\n+        description: \"The C++ frontend\"\n+        match:\n+          file: \"^/gcc/c(p|-family)/\"\n+      - name: \"cobol\"\n+        description: \"The COBOL frontend\"\n+        match:\n+          file: \"^/gcc/cobol/\"\n+      - name: \"d\"\n+        description: \"The D frontend\"\n+        match:\n+          file: \"^/gcc/d/\"\n+      - name: \"fortran\"\n+        description: \"The Fortran frontend\"\n+        match:\n+          file: \"^/gcc/fortran/\"\n+      - name: \"go\"\n+        description: \"The Go frontend\"\n+        match:\n+          file: \"^/gcc/(go/|godump*)\"\n+      - name: \"m2\"\n+        description: \"The Modula-2 frontend\"\n+        match:\n+          file: \"^/gcc/m2/\"\n+      - name: \"objc\"\n+        description: \"The Objective-C/-C++ frontends\"\n+        match:\n+          file: \"^/gcc/objcp?/\"\n+      - name: \"rust\"\n+        description: \"The Rust frontend\"\n+        match:\n+          file: \"^/gcc/rust/\"\n+  # Labels relating to testsuites (probably incomplete)\n+  - group: \"Tests\"\n+    exclusive: false\n+    color: \"c544ff\"\n+    match_defaults:\n+      class: \"file\"\n+    labels:\n+      - name: \"framework\"\n+        description: \"Changes to the test framework (testsuite/lib)\"\n+        match:\n+          file: \"testsuite/lib/\"\n+      - name: \"torture\"\n+        description: \"Changes to legacy torture tests\"\n+        match:\n+          file: \"^/gcc/testsuite/*-torture/\"\n+      - name: \"target\"\n+        description: \"Changes to target-specific tests (use target/* as well)\"\n+        match:\n+          file: \"^/gcc/testsuite/*.target/\"\n+      - name: \"lang\"\n+        description: \"Changes to language-specific tests\"\n+        match:\n+          file: \"^/gcc/testsuite/(gcc|gdc|gfortran|g++|obj|go|cobol|gnat|ada|c-c++|gm2|rust)*/\"\n+      - name: \"lib\"\n+        description: \"Changes to top-level library tests (use Library/* as well)\"\n+        match:\n+          file: \"^/lib*/**/testsuite/\"\n+      - name: \"jit\"\n+        description: \"Changes to GCC's jit tests\"\n+        match:\n+          file: \"^/gcc/testsuite/jit.dg/\"\n+      - name: \"selftest\"\n+        description: \"Changes to GCC's self-tests\"\n+        match:\n+          file: \"^/gcc/testsuite/selftests/\"\n+      - name: \"diagnostics\"\n+        description: \"Changes to GCC's diagnostic framework tests\"\n+        match:\n+          file: \"^/gcc/testsuite/libgdiagnostics.dg/\"\n+      - name: \"sarif\"\n+        description: \"Changes to GCC's sarif tests\"\n+        match:\n+          file: \"^/gcc/testsuite/sarif-replay.dg/\"\n+\n+  # # Forge status labels\n+  # These have been removed for now.  It's not clear how useful\n+  # these would be, given that we may have more than one runner\n+  #\n+  # # Labels for runners (exclusive)\n+  # - group: \"Runners\"\n+  #   exclusive: true\n+  #   color: \"f6c6c7\"\n+  #   match_defaults:\n+  #     class: \"none\"\n+  #   labels:\n+  #     - name: \"pending\"\n+  #       description: \"Runner has started, but results not yet available\"\n+  #     - name: \"unresolved\"\n+  #       description: \"Runner failed for an unknown reason\"\n+  #     - name: \"red\"\n+  #       description: \"Check failed with errors, code not fit for committing\"\n+  #     - name: \"amber\"\n+  #       description: \"Check failed with warnings, manually inspect results\"\n+  #     - name: \"green\"\n+  #       description: \"Checks passed\"\n+  - group: \"Reviewed\"\n+    exclusive: true\n+    color: \"616161\"\n+    match_defaults:\n+      class: \"file\"\n+    labels:\n+      - name: Confirmed\n+        description: \"Issue has been confirmed\"\ndiff --git a/contrib/forge/update-labels.py b/contrib/forge/update-labels.py\nnew file mode 100755\nindex 00000000000..b8e81b617e4\n--- /dev/null\n+++ b/contrib/forge/update-labels.py\n@@ -0,0 +1,151 @@\n+#! /usr/bin/env python3\n+# Update the list of labels on a Forgejo instance\n+\n+# Copyright (C) 2026 Free Software Foundation, Inc.\n+#\n+# This file is part of GCC.\n+#\n+# GCC is free software; you can redistribute it and/or modify it under\n+# the terms of the GNU General Public License as published by the Free\n+# Software Foundation; either version 3, or (at your option) any later\n+# version.\n+#\n+# GCC is distributed in the hope that it will be useful, but WITHOUT ANY\n+# WARRANTY; without even the implied warranty of MERCHANTABILITY or\n+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n+# for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with GCC; see the file COPYING3.  If not see\n+# <http://www.gnu.org/licenses/>.\n+\n+# Variables needed by the script.  Can be overridden by setting the\n+# same name in the environment\n+\n+PROJECT = \"gcc\"\n+REPO    = \"gcc-test\"\n+FORGE  = \"https://forge.sourceware.org/api/v1\"\n+\n+APIKEY  = None\n+\n+import os, sys, forgelabels, urllib.request, json, getpass\n+\n+def setup():\n+    global PROJECT, REPO, FORGE, APIKEY\n+    PROJECT = os.getenv(\"PROJECT\", PROJECT)\n+    REPO = os.getenv(\"REPO\", REPO)\n+    FORGE = os.getenv(\"FORGE\", FORGE)\n+    APIKEY = getpass.getpass(prompt=\"API key: \")\n+    print (f\"Accessing {FORGE}/{PROJECT}/{REPO}.\")\n+\n+def read_existing():\n+    global FORGE, PROJECT, REPO\n+    try:\n+        url = f\"{FORGE}/repos/{PROJECT}/{REPO}/labels\"\n+        reply = urllib.request.urlopen(url)\n+        labels_list = json.loads(reply.read())\n+    except Exception as e:\n+        print (f\"Error reading label data: {e}\")\n+        sys.exit (1)\n+    return labels_list\n+\n+def add_label(l):\n+    global FORGE, PROJECT, REPO, APIKEY\n+    payload = {'name': l.full_name,\n+               'color': l.color(),\n+               'exclusive': l.group.exclusive,\n+               'is_archived': False}\n+    if l.desc != None:\n+        payload['description'] = l.desc\n+    headers = {'Authorization': f\"token {APIKEY}\",\n+               'accept': \"application/json\",\n+               'Content-Type': \"application/json\"} \n+    try:\n+        url = f\"{FORGE}/repos/{PROJECT}/{REPO}/labels\"\n+        payload = json.dumps (payload).encode(\"utf-8\")\n+        request = urllib.request.Request (url, data = payload,\n+                                          headers = headers,\n+                                          method = 'POST')\n+        reply = urllib.request.urlopen(request)\n+    except Exception as e:\n+        print (f\"Error writing label data: {e}\")\n+        sys.exit (1)\n+    print (f\"Success: add {l.full_name}\")\n+\n+def modify_label(o, l):\n+    global FORGE, PROJECT, REPO, APIKEY\n+    payload = {'name': l.full_name,\n+               'color': l.color(),\n+               'exclusive': l.group.exclusive,\n+               'is_archived': False}\n+    if l.desc != None:\n+        payload['description'] = l.desc\n+    headers = {'Authorization': f\"token {APIKEY}\",\n+               'accept': \"application/json\",\n+               'Content-Type': \"application/json\"} \n+    try:\n+        url = f\"{FORGE}/repos/{PROJECT}/{REPO}/labels/{o['id']}\"\n+        payload = json.dumps (payload).encode(\"utf-8\")\n+        request = urllib.request.Request (url, data = payload,\n+                                          headers = headers,\n+                                          method = 'PATCH')\n+        reply = urllib.request.urlopen(request)\n+    except Exception as e:\n+        print (f\"Error writing label data: {e}\")\n+        sys.exit (1)\n+    print (f\"Success: modify {l.full_name}\")\n+\n+def archive_label(o):\n+    global FORGE, PROJECT, REPO, APIKEY\n+    payload = {'name': o['name'],\n+               'is_archived': True}\n+    headers = {'Authorization': f\"token {APIKEY}\",\n+               'accept': \"application/json\",\n+               'Content-Type': \"application/json\"} \n+    try:\n+        url = f\"{FORGE}/repos/{PROJECT}/{REPO}/labels/{o['id']}\"\n+        payload = json.dumps (payload).encode(\"utf-8\")\n+        request = urllib.request.Request (url, data = payload,\n+                                          headers = headers,\n+                                          method = 'PATCH')\n+        reply = urllib.request.urlopen(request)\n+    except Exception as e:\n+        print (f\"Error writing label data: {e}\")\n+        sys.exit (1)\n+    print (f\"Success: archive {o['name']}\")\n+\n+def main():\n+    setup()\n+    groups = forgelabels.load()\n+    oldlabels = read_existing()\n+    toadd = []\n+    tochange = []\n+\n+    for g in groups:\n+        for l in g.labels:\n+            matched = False\n+            for o in oldlabels:\n+                if o['name'] == l.full_name:\n+                    if (o['is_archived']\n+                        or o['color'] != l.color()\n+                        or o['exclusive'] != l.group.exclusive\n+                        or o['description'] != l.desc):\n+                        tochange.append((o, l))\n+                    matched = True\n+                    o['matched'] = True\n+                    break\n+            if not matched:\n+                toadd.append(l)\n+    for o in oldlabels:\n+        if not 'matched' in o and not o['is_archived']:\n+            archive_label(o)\n+            print (f\"To archive: {o['name']}\")\n+    for o, n in tochange:\n+        modify_label (o, n)\n+    for l in toadd:\n+        add_label (l)\n+\n+if __name__ == \"__main__\":\n+    main()\n+\n+    \n",
    "prefixes": [
        "RFD"
    ]
}