[PATCH] [pre-commit] Don't allow pre-release revisions
Tom de Vries <[email protected]>
| Newsgroups | gmane.comp.gdb.patches |
|---|---|
| Message-ID | <[email protected]> |
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