Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH] [pre-commit] Don't allow pre-release revisions
@ 2026-08-13 15:28 Tom de Vries
  2026-08-27  9:21 ` Tom de Vries
  0 siblings, 1 reply; 2+ messages in thread
From: Tom de Vries @ 2026-08-13 15:28 UTC (permalink / raw)
  To: gdb-patches

Pre-commit has a convenient autoupdate command.  The default behavior is:
update to the latest tagged version.

For most tools this is fine, and not too frequent.

Isort however also tags pre-releases, so that has resulted in more frequent
updates:
...
$ git log .pre-commit-config.yaml | grep "Bump isort"
    [pre-commit] Bump isort to 9.0.0b1
    [pre-commit] Bump isort to 9.0.0a3
    [pre-commit] Bump isort to 9.0.0a2
    [pre-commit] Bump isort to 8.0.1
    [pre-commit] Bump isort to 8.0.0
...

Add a script gdb/contrib/pre-commit.py that for --config-check checks the
revision numbers of all repos, and errors out when finding something like
9.0.0b1:
...
$ ./gdb/contrib/pre-commit.py --config-check
Revision 9.0.0b1 for repo https://github.com/pycqa/isort not allowed.
...

Add a corresponding pre-commit check, and revert isort back to the last stable
release: 8.0.1, to make the check pass.

Consequently, pre-commit autoupdate now breaks the pre-commit check:
...
$ pre-commit autoupdate
[https://github.com/psf/black-pre-commit-mirror] already up to date!
[https://github.com/pycqa/flake8] already up to date!
[https://github.com/pycqa/isort] updating 8.0.1 -> 9.0.0b2
[https://github.com/codespell-project/codespell] already up to date!
[https://github.com/nmoroze/tclint] already up to date!
[https://github.com/adrienverge/yamllint.git] already up to date!
$ ./gdb/contrib/pre-commit.py --config-check
Revision 9.0.0b2 for repo https://github.com/pycqa/isort not allowed.
...
so add gdb/contrib/pre-commit.py --autoupdate that can be used instead:
...
$ ./gdb/contrib/pre-commit.py --autoupdate
[https://github.com/psf/black-pre-commit-mirror] already up to date!
[https://github.com/pycqa/flake8] already up to date!
[https://github.com/pycqa/isort] updating 8.0.1 -> 9.0.0b2
Revision 9.0.0b2 for repo https://github.com/pycqa/isort not allowed.
[https://github.com/codespell-project/codespell] already up to date!
[https://github.com/nmoroze/tclint] already up to date!
[https://github.com/adrienverge/yamllint.git] already up to date!
...

The script could be extended to check that frozen revs stay frozen, but that's
currently not needed.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34393
---
 .pre-commit-config.yaml   |   9 ++-
 gdb/contrib/pre-commit.py | 154 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 162 insertions(+), 1 deletion(-)
 create mode 100755 gdb/contrib/pre-commit.py

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3cccec45ef7..7b97b9a0ce8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -70,7 +70,7 @@ repos:
         files: *gdb_python_files
         args: [--config, gdb/setup.cfg]
   - repo: https://github.com/pycqa/isort
-    rev: 9.0.0b1
+    rev: 8.0.1
     hooks:
       - id: isort
         types_or: *gdb_python_types
@@ -147,6 +147,13 @@ repos:
         language: unsupported_script
         entry: gdb/contrib/check-file-mode.sh
         files: *gdb_files
+      - id: &id5 pre-commit-config-check
+        name: *id5
+        language: python
+        entry: gdb/contrib/pre-commit.py
+        args: [--config-check]
+        additional_dependencies: ["pyyaml"]
+        files: '^\.pre-commit-config.yaml$'
 
 # Local Variables:
 # indent-tabs-mode: nil
diff --git a/gdb/contrib/pre-commit.py b/gdb/contrib/pre-commit.py
new file mode 100755
index 00000000000..9ab746c7355
--- /dev/null
+++ b/gdb/contrib/pre-commit.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+# This program 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import subprocess
+import sys
+
+import yaml
+
+
+def get_repos(cfg):
+    with open(cfg, "r") as f:
+        data = yaml.safe_load(f)
+        repos = data.get("repos")
+        if not repos:
+            raise RuntimeError("repos missing")
+        return repos
+
+
+def config_check_repo(repo):
+    name = repo.get("repo")
+    if not name:
+        raise RuntimeError("no repo")
+    if name == "local":
+        # Skip local repo, there's no revision to check.
+        return True
+
+    # Get the revision.
+    rev = repo["rev"]
+    if not rev or rev == "":
+        raise RuntimeError("empty revision")
+
+    # Normalize revision: skip 'v' prefix.
+    if rev[0].lower() == "v":
+        rev = rev[1:]
+
+    # Check version number.  Don't allow pre-releases like 9.0.0b1.
+    # We currently only need to support x.y.z, but that could change.
+    if not re.fullmatch(r"\d+[.]\d+[.]\d+", rev):
+        print("Revision %s for repo %s not allowed." % (rev, name))
+        return False
+
+    return True
+
+
+def config_check(cfg):
+    for repo in get_repos(cfg):
+        if not config_check_repo(repo):
+            sys.exit(1)
+
+
+def run_cmd(cmd, **kwargs):
+    res = subprocess.run(cmd, **kwargs)
+    if res.returncode != 0:
+        raise RuntimeError(
+            "command %s failed with exit status %s" % (cmd, res.returncode)
+        )
+    return res
+
+
+def autoupdate_repo(cfg, repo):
+    name = repo["repo"]
+    if name == "local":
+        # Skip local repo, there's no revision to update.
+        return
+
+    cmd = ["pre-commit", "autoupdate", "--repo", name]
+    run_cmd(cmd)
+
+    cmd = ["git", "status", "--porcelain", cfg]
+    res = run_cmd(cmd, capture_output=True, text=True)
+    if res.stdout == "":
+        # No autoupdate changes.
+        return
+
+    rev = repo["rev"]
+
+    # Config has changed, refresh repo.
+    found = False
+    for new_repo in get_repos(cfg):
+        if new_repo["repo"] == name:
+            found = True
+            break
+    if not found:
+        raise RuntimeError("Repo not found in updated %s" % cfg)
+    repo = new_repo
+
+    new_rev = repo["rev"]
+
+    if not config_check_repo(repo):
+        # Reject autoupdate for this repo.
+        cmd = ["git", "checkout", "-f", cfg]
+        # Capture and ignore output.
+        run_cmd(cmd, capture_output=True, text=True)
+        return
+
+    # Commit autoupdate for this repo.
+    try:
+        name_for_msg = repo["hooks"][0]["id"]
+    except (KeyError, IndexError):
+        name_for_msg = name
+    msg = "Update %s: %s -> %s" % (name_for_msg, rev, new_rev)
+    cmd = ["git", "commit", "-m", msg, cfg]
+    run_cmd(cmd)
+
+
+def autoupdate(cfg):
+    for repo in get_repos(cfg):
+        autoupdate_repo(cfg, repo)
+
+
+def usage():
+    print("Usage: pre-commit.py --config-check [<file>]")
+    print("                     --autoupdate")
+    sys.exit(1)
+
+
+def main():
+    if len(sys.argv) < 2:
+        usage()
+
+    cfg = ".pre-commit-config.yaml"
+
+    if sys.argv[1] == "--config-check":
+        if len(sys.argv) not in [2, 3]:
+            usage()
+        if len(sys.argv) == 3:
+            cfg = sys.argv[2]
+        config_check(cfg)
+        return
+
+    if sys.argv[1] == "--autoupdate":
+        if len(sys.argv) != 2:
+            usage()
+        autoupdate(cfg)
+        return
+
+    usage()
+
+
+main()

base-commit: 1686e21559a7812ebbc05f57372cc30880768bf7
-- 
2.51.0


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

* Re: [PATCH] [pre-commit] Don't allow pre-release revisions
  2026-08-13 15:28 [PATCH] [pre-commit] Don't allow pre-release revisions Tom de Vries
@ 2026-08-27  9:21 ` Tom de Vries
  0 siblings, 0 replies; 2+ messages in thread
From: Tom de Vries @ 2026-08-27  9:21 UTC (permalink / raw)
  To: gdb-patches

On 8/13/26 5:28 PM, Tom de Vries wrote:
> Pre-commit has a convenient autoupdate command.  The default behavior is:
> update to the latest tagged version.
> 
> For most tools this is fine, and not too frequent.
> 
> Isort however also tags pre-releases, so that has resulted in more frequent
> updates:
> ...
> $ git log .pre-commit-config.yaml | grep "Bump isort"
>      [pre-commit] Bump isort to 9.0.0b1
>      [pre-commit] Bump isort to 9.0.0a3
>      [pre-commit] Bump isort to 9.0.0a2
>      [pre-commit] Bump isort to 8.0.1
>      [pre-commit] Bump isort to 8.0.0
> ...
> 
> Add a script gdb/contrib/pre-commit.py that for --config-check checks the
> revision numbers of all repos, and errors out when finding something like
> 9.0.0b1:
> ...
> $ ./gdb/contrib/pre-commit.py --config-check
> Revision 9.0.0b1 for repo https://github.com/pycqa/isort not allowed.
> ...
> 
> Add a corresponding pre-commit check, and revert isort back to the last stable
> release: 8.0.1, to make the check pass.
> 
> Consequently, pre-commit autoupdate now breaks the pre-commit check:
> ...
> $ pre-commit autoupdate
> [https://github.com/psf/black-pre-commit-mirror] already up to date!
> [https://github.com/pycqa/flake8] already up to date!
> [https://github.com/pycqa/isort] updating 8.0.1 -> 9.0.0b2
> [https://github.com/codespell-project/codespell] already up to date!
> [https://github.com/nmoroze/tclint] already up to date!
> [https://github.com/adrienverge/yamllint.git] already up to date!
> $ ./gdb/contrib/pre-commit.py --config-check
> Revision 9.0.0b2 for repo https://github.com/pycqa/isort not allowed.
> ...
> so add gdb/contrib/pre-commit.py --autoupdate that can be used instead:
> ...
> $ ./gdb/contrib/pre-commit.py --autoupdate
> [https://github.com/psf/black-pre-commit-mirror] already up to date!
> [https://github.com/pycqa/flake8] already up to date!
> [https://github.com/pycqa/isort] updating 8.0.1 -> 9.0.0b2
> Revision 9.0.0b2 for repo https://github.com/pycqa/isort not allowed.
> [https://github.com/codespell-project/codespell] already up to date!
> [https://github.com/nmoroze/tclint] already up to date!
> [https://github.com/adrienverge/yamllint.git] already up to date!
> ...
> 
> The script could be extended to check that frozen revs stay frozen, but that's
> currently not needed.
> 

I've pushed this, after fixing some nits Claude Code pointed out.

Then I ran the script, and pushed the resulting commit updating isort to 
9.0.0.

Thanks,
- Tom

> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34393
> ---
>   .pre-commit-config.yaml   |   9 ++-
>   gdb/contrib/pre-commit.py | 154 ++++++++++++++++++++++++++++++++++++++
>   2 files changed, 162 insertions(+), 1 deletion(-)
>   create mode 100755 gdb/contrib/pre-commit.py
> 
> diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
> index 3cccec45ef7..7b97b9a0ce8 100644
> --- a/.pre-commit-config.yaml
> +++ b/.pre-commit-config.yaml
> @@ -70,7 +70,7 @@ repos:
>           files: *gdb_python_files
>           args: [--config, gdb/setup.cfg]
>     - repo: https://github.com/pycqa/isort
> -    rev: 9.0.0b1
> +    rev: 8.0.1
>       hooks:
>         - id: isort
>           types_or: *gdb_python_types
> @@ -147,6 +147,13 @@ repos:
>           language: unsupported_script
>           entry: gdb/contrib/check-file-mode.sh
>           files: *gdb_files
> +      - id: &id5 pre-commit-config-check
> +        name: *id5
> +        language: python
> +        entry: gdb/contrib/pre-commit.py
> +        args: [--config-check]
> +        additional_dependencies: ["pyyaml"]
> +        files: '^\.pre-commit-config.yaml$'
>   
>   # Local Variables:
>   # indent-tabs-mode: nil
> diff --git a/gdb/contrib/pre-commit.py b/gdb/contrib/pre-commit.py
> new file mode 100755
> index 00000000000..9ab746c7355
> --- /dev/null
> +++ b/gdb/contrib/pre-commit.py
> @@ -0,0 +1,154 @@
> +#!/usr/bin/env python3
> +
> +# Copyright (C) 2026 Free Software Foundation, Inc.
> +# This program 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 of the License, or
> +# (at your option) any later version.
> +#
> +# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +import re
> +import subprocess
> +import sys
> +
> +import yaml
> +
> +
> +def get_repos(cfg):
> +    with open(cfg, "r") as f:
> +        data = yaml.safe_load(f)
> +        repos = data.get("repos")
> +        if not repos:
> +            raise RuntimeError("repos missing")
> +        return repos
> +
> +
> +def config_check_repo(repo):
> +    name = repo.get("repo")
> +    if not name:
> +        raise RuntimeError("no repo")
> +    if name == "local":
> +        # Skip local repo, there's no revision to check.
> +        return True
> +
> +    # Get the revision.
> +    rev = repo["rev"]
> +    if not rev or rev == "":
> +        raise RuntimeError("empty revision")
> +
> +    # Normalize revision: skip 'v' prefix.
> +    if rev[0].lower() == "v":
> +        rev = rev[1:]
> +
> +    # Check version number.  Don't allow pre-releases like 9.0.0b1.
> +    # We currently only need to support x.y.z, but that could change.
> +    if not re.fullmatch(r"\d+[.]\d+[.]\d+", rev):
> +        print("Revision %s for repo %s not allowed." % (rev, name))
> +        return False
> +
> +    return True
> +
> +
> +def config_check(cfg):
> +    for repo in get_repos(cfg):
> +        if not config_check_repo(repo):
> +            sys.exit(1)
> +
> +
> +def run_cmd(cmd, **kwargs):
> +    res = subprocess.run(cmd, **kwargs)
> +    if res.returncode != 0:
> +        raise RuntimeError(
> +            "command %s failed with exit status %s" % (cmd, res.returncode)
> +        )
> +    return res
> +
> +
> +def autoupdate_repo(cfg, repo):
> +    name = repo["repo"]
> +    if name == "local":
> +        # Skip local repo, there's no revision to update.
> +        return
> +
> +    cmd = ["pre-commit", "autoupdate", "--repo", name]
> +    run_cmd(cmd)
> +
> +    cmd = ["git", "status", "--porcelain", cfg]
> +    res = run_cmd(cmd, capture_output=True, text=True)
> +    if res.stdout == "":
> +        # No autoupdate changes.
> +        return
> +
> +    rev = repo["rev"]
> +
> +    # Config has changed, refresh repo.
> +    found = False
> +    for new_repo in get_repos(cfg):
> +        if new_repo["repo"] == name:
> +            found = True
> +            break
> +    if not found:
> +        raise RuntimeError("Repo not found in updated %s" % cfg)
> +    repo = new_repo
> +
> +    new_rev = repo["rev"]
> +
> +    if not config_check_repo(repo):
> +        # Reject autoupdate for this repo.
> +        cmd = ["git", "checkout", "-f", cfg]
> +        # Capture and ignore output.
> +        run_cmd(cmd, capture_output=True, text=True)
> +        return
> +
> +    # Commit autoupdate for this repo.
> +    try:
> +        name_for_msg = repo["hooks"][0]["id"]
> +    except (KeyError, IndexError):
> +        name_for_msg = name
> +    msg = "Update %s: %s -> %s" % (name_for_msg, rev, new_rev)
> +    cmd = ["git", "commit", "-m", msg, cfg]
> +    run_cmd(cmd)
> +
> +
> +def autoupdate(cfg):
> +    for repo in get_repos(cfg):
> +        autoupdate_repo(cfg, repo)
> +
> +
> +def usage():
> +    print("Usage: pre-commit.py --config-check [<file>]")
> +    print("                     --autoupdate")
> +    sys.exit(1)
> +
> +
> +def main():
> +    if len(sys.argv) < 2:
> +        usage()
> +
> +    cfg = ".pre-commit-config.yaml"
> +
> +    if sys.argv[1] == "--config-check":
> +        if len(sys.argv) not in [2, 3]:
> +            usage()
> +        if len(sys.argv) == 3:
> +            cfg = sys.argv[2]
> +        config_check(cfg)
> +        return
> +
> +    if sys.argv[1] == "--autoupdate":
> +        if len(sys.argv) != 2:
> +            usage()
> +        autoupdate(cfg)
> +        return
> +
> +    usage()
> +
> +
> +main()
> 
> base-commit: 1686e21559a7812ebbc05f57372cc30880768bf7


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

end of thread, other threads:[~2026-08-27  9:22 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-13 15:28 [PATCH] [pre-commit] Don't allow pre-release revisions Tom de Vries
2026-08-27  9:21 ` Tom de Vries

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