gha-utils 4.20.0__tar.gz → 4.22.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gha-utils might be problematic. Click here for more details.

Files changed (23) hide show
  1. {gha_utils-4.20.0 → gha_utils-4.22.0}/PKG-INFO +2 -2
  2. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils/__init__.py +1 -1
  3. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils/cli.py +3 -3
  4. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils/metadata.py +128 -50
  5. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils/test_plan.py +6 -4
  6. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils.egg-info/PKG-INFO +2 -2
  7. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils.egg-info/requires.txt +1 -1
  8. {gha_utils-4.20.0 → gha_utils-4.22.0}/pyproject.toml +6 -4
  9. {gha_utils-4.20.0 → gha_utils-4.22.0}/tests/test_metadata.py +57 -36
  10. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils/__main__.py +0 -0
  11. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils/changelog.py +0 -0
  12. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils/mailmap.py +0 -0
  13. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils/matrix.py +0 -0
  14. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils/py.typed +0 -0
  15. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils.egg-info/SOURCES.txt +0 -0
  16. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils.egg-info/dependency_links.txt +0 -0
  17. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils.egg-info/entry_points.txt +0 -0
  18. {gha_utils-4.20.0 → gha_utils-4.22.0}/gha_utils.egg-info/top_level.txt +0 -0
  19. {gha_utils-4.20.0 → gha_utils-4.22.0}/readme.md +0 -0
  20. {gha_utils-4.20.0 → gha_utils-4.22.0}/setup.cfg +0 -0
  21. {gha_utils-4.20.0 → gha_utils-4.22.0}/tests/test_changelog.py +0 -0
  22. {gha_utils-4.20.0 → gha_utils-4.22.0}/tests/test_mailmap.py +0 -0
  23. {gha_utils-4.20.0 → gha_utils-4.22.0}/tests/test_matrix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gha-utils
3
- Version: 4.20.0
3
+ Version: 4.22.0
4
4
  Summary: ⚙️ CLI helpers for GitHub Actions + reuseable workflows
5
5
  Author-email: Kevin Deldycke <kevin@deldycke.com>
6
6
  Project-URL: Homepage, https://github.com/kdeldycke/workflows
@@ -50,7 +50,7 @@ Requires-Python: >=3.11
50
50
  Description-Content-Type: text/markdown
51
51
  Requires-Dist: boltons>=24.0.0
52
52
  Requires-Dist: bump-my-version<1.1.1,>=0.32.2
53
- Requires-Dist: click-extra~=6.0.0
53
+ Requires-Dist: click-extra~=6.0.2
54
54
  Requires-Dist: extra-platforms~=3.2.0
55
55
  Requires-Dist: packaging~=25.0
56
56
  Requires-Dist: py-walk~=0.3.3
@@ -17,4 +17,4 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = "4.20.0"
20
+ __version__ = "4.22.0"
@@ -239,9 +239,9 @@ def changelog(ctx, source, changelog_path):
239
239
  "--create-if-missing/--skip-if-missing",
240
240
  is_flag=True,
241
241
  default=True,
242
- help="Create the destination mailmap file if it is not found. Or skip the update "
243
- "process entirely if if does not already exists in the first place. This option "
244
- f"is ignore if the destination is to print the result to {sys.stdout.name}.",
242
+ help="If not found, either create the missing destination mailmap file, or skip "
243
+ "the update process entirely. This option is ignored if the destination is to print "
244
+ f"the result to {sys.stdout.name}.",
245
245
  )
246
246
  @argument(
247
247
  "destination_mailmap",
@@ -23,9 +23,16 @@ The following variables are `printed to the environment file
23
23
  is_bot=false
24
24
  new_commits=346ce664f055fbd042a25ee0b7e96702e95 6f27db47612aaee06fdf08744b09a9f5f6c2
25
25
  release_commits=6f27db47612aaee06fdf08744b09a9f5f6c2
26
+ mailmap_exists=true
26
27
  gitignore_exists=true
27
28
  python_files=".github/update_mailmap.py" ".github/metadata.py" "setup.py"
29
+ json_files=
30
+ yaml_files="config.yaml" ".github/workflows/lint.yaml" ".github/workflows/test.yaml"
31
+ workflow_files=".github/workflows/lint.yaml" ".github/workflows/test.yaml"
28
32
  doc_files="changelog.md" "readme.md" "docs/license.md"
33
+ markdown_files="changelog.md" "readme.md" "docs/license.md"
34
+ image_files=
35
+ zsh_files=
29
36
  is_python_project=true
30
37
  package_name=click-extra
31
38
  blacken_docs_params=--target-version py37 --target-version py38
@@ -80,14 +87,14 @@ build_targets=[
80
87
  },
81
88
  {
82
89
  "target": "macos-arm64",
83
- "os": "macos-15",
90
+ "os": "macos-26",
84
91
  "platform_id": "macos",
85
92
  "arch": "arm64",
86
93
  "extension": "bin"
87
94
  },
88
95
  {
89
96
  "target": "macos-x64",
90
- "os": "macos-13",
97
+ "os": "macos-15-intel",
91
98
  "platform_id": "macos",
92
99
  "arch": "x64",
93
100
  "extension": "bin"
@@ -111,8 +118,8 @@ nuitka_matrix={
111
118
  "os": [
112
119
  "ubuntu-24.04-arm",
113
120
  "ubuntu-24.04",
114
- "macos-15",
115
- "macos-13",
121
+ "macos-26",
122
+ "macos-15-intel",
116
123
  "windows-11-arm",
117
124
  "windows-2025"
118
125
  ],
@@ -138,14 +145,14 @@ nuitka_matrix={
138
145
  },
139
146
  {
140
147
  "target": "macos-arm64",
141
- "os": "macos-15",
148
+ "os": "macos-26",
142
149
  "platform_id": "macos",
143
150
  "arch": "arm64",
144
151
  "extension": "bin"
145
152
  },
146
153
  {
147
154
  "target": "macos-x64",
148
- "os": "macos-13",
155
+ "os": "macos-15-intel",
149
156
  "platform_id": "macos",
150
157
  "arch": "x64",
151
158
  "extension": "bin"
@@ -206,25 +213,25 @@ nuitka_matrix={
206
213
  "bin_name": "mpm-linux-x64-6f27db4.bin"
207
214
  },
208
215
  {
209
- "os": "macos-15",
216
+ "os": "macos-26",
210
217
  "entry_point": "mpm",
211
218
  "commit": "346ce664f055fbd042a25ee0b7e96702e95",
212
219
  "bin_name": "mpm-macos-arm64-346ce66.bin"
213
220
  },
214
221
  {
215
- "os": "macos-15",
222
+ "os": "macos-26",
216
223
  "entry_point": "mpm",
217
224
  "commit": "6f27db47612aaee06fdf08744b09a9f5f6c2",
218
225
  "bin_name": "mpm-macos-arm64-6f27db4.bin"
219
226
  },
220
227
  {
221
- "os": "macos-13",
228
+ "os": "macos-15-intel",
222
229
  "entry_point": "mpm",
223
230
  "commit": "346ce664f055fbd042a25ee0b7e96702e95",
224
231
  "bin_name": "mpm-macos-x64-346ce66.bin"
225
232
  },
226
233
  {
227
- "os": "macos-13",
234
+ "os": "macos-15-intel",
228
235
  "entry_point": "mpm",
229
236
  "commit": "6f27db47612aaee06fdf08744b09a9f5f6c2",
230
237
  "bin_name": "mpm-macos-x64-6f27db4.bin"
@@ -288,6 +295,7 @@ from extra_platforms import is_github_ci
288
295
  from packaging.specifiers import SpecifierSet
289
296
  from packaging.version import Version
290
297
  from py_walk import get_parser_from_file
298
+ from py_walk.models import Parser
291
299
  from pydriller import Commit, Git, Repository # type: ignore[import-untyped]
292
300
  from pyproject_metadata import ConfigurationError, StandardMetadata
293
301
  from wcmatch.glob import (
@@ -312,6 +320,8 @@ SHORT_SHA_LENGTH = 7
312
320
  depends on the size of the repository.
313
321
  """
314
322
 
323
+ MAILMAP_PATH = Path(".mailmap")
324
+
315
325
  GITIGNORE_PATH = Path(".gitignore")
316
326
 
317
327
  NUITKA_BUILD_TARGETS = {
@@ -328,13 +338,13 @@ NUITKA_BUILD_TARGETS = {
328
338
  "extension": "bin",
329
339
  },
330
340
  "macos-arm64": {
331
- "os": "macos-15",
341
+ "os": "macos-26",
332
342
  "platform_id": "macos",
333
343
  "arch": "arm64",
334
344
  "extension": "bin",
335
345
  },
336
346
  "macos-x64": {
337
- "os": "macos-13",
347
+ "os": "macos-15-intel",
338
348
  "platform_id": "macos",
339
349
  "arch": "x64",
340
350
  "extension": "bin",
@@ -472,6 +482,10 @@ MYPY_VERSION_MIN: Final = (3, 8)
472
482
  """
473
483
 
474
484
 
485
+ # Silence overly verbose debug messages from py-walk logger.
486
+ logging.getLogger("py_walk").setLevel(logging.WARNING)
487
+
488
+
475
489
  class JSONMetadata(json.JSONEncoder):
476
490
  """Custom JSON encoder for metadata serialization."""
477
491
 
@@ -531,10 +545,15 @@ class Metadata:
531
545
  logging.debug(json.dumps(context, indent=4))
532
546
  return context # type:ignore[no-any-return]
533
547
 
534
- def git_stash_count(self, git_repo: Git) -> int:
548
+ @cached_property
549
+ def git(self) -> Git:
550
+ """Return a PyDriller Git object."""
551
+ return Git(".")
552
+
553
+ def git_stash_count(self) -> int:
535
554
  """Returns the number of stashes."""
536
555
  count = int(
537
- git_repo.repo.git.rev_list(
556
+ self.git.repo.git.rev_list(
538
557
  "--walk-reflogs", "--ignore-missing", "--count", "refs/stash"
539
558
  )
540
559
  )
@@ -579,8 +598,7 @@ class Metadata:
579
598
  if not commits:
580
599
  return None
581
600
 
582
- git = Git(".")
583
- current_commit = git.repo.head.commit.hexsha
601
+ current_commit = self.git.repo.head.commit.hexsha
584
602
 
585
603
  # Check if we need to get back in time in the Git log and browse past commits.
586
604
  if len(commits) == 1: # type: ignore[arg-type]
@@ -608,17 +626,17 @@ class Metadata:
608
626
  # Save the initial commit reference and SHA of the repository. The
609
627
  # reference is either the canonical active branch name (i.e. ``main``), or
610
628
  # the commit SHA if the current HEAD commit is detached from a branch.
611
- if git.repo.head.is_detached:
629
+ if self.git.repo.head.is_detached:
612
630
  init_ref = current_commit
613
631
  else:
614
- init_ref = git.repo.active_branch.name
632
+ init_ref = self.git.repo.active_branch.name
615
633
  logging.debug(f"Initial commit reference: {init_ref}")
616
634
 
617
635
  # Try to stash local changes and check if we'll need to unstash them later.
618
- counter_before = self.git_stash_count(git)
636
+ counter_before = self.git_stash_count()
619
637
  logging.debug("Try to stash local changes before our series of checkouts.")
620
- git.repo.git.stash()
621
- counter_after = self.git_stash_count(git)
638
+ self.git.repo.git.stash()
639
+ counter_after = self.git_stash_count()
622
640
  logging.debug(
623
641
  "Stash counter changes after 'git stash' command: "
624
642
  f"{counter_before} -> {counter_after}"
@@ -639,7 +657,7 @@ class Metadata:
639
657
  for commit in commits:
640
658
  if past_commit_lookup:
641
659
  logging.debug(f"Checkout to commit {commit.hash}")
642
- git.checkout(commit.hash)
660
+ self.git.checkout(commit.hash)
643
661
 
644
662
  commit_metadata = {
645
663
  "commit": commit.hash,
@@ -657,10 +675,10 @@ class Metadata:
657
675
  # Restore the repository to its initial state.
658
676
  if past_commit_lookup:
659
677
  logging.debug(f"Restore repository to {init_ref}.")
660
- git.checkout(init_ref)
678
+ self.git.checkout(init_ref)
661
679
  if need_unstash:
662
680
  logging.debug("Unstash local changes that were previously saved.")
663
- git.repo.git.stash("pop")
681
+ self.git.repo.git.stash("pop")
664
682
 
665
683
  return matrix
666
684
 
@@ -719,7 +737,7 @@ class Metadata:
719
737
  return False
720
738
 
721
739
  @cached_property
722
- def commit_range(self) -> tuple[str, str] | None:
740
+ def commit_range(self) -> tuple[str | None, str] | None:
723
741
  """Range of commits bundled within the triggering event.
724
742
 
725
743
  A workflow run is triggered by a singular event, which might encapsulate one or
@@ -766,7 +784,7 @@ class Metadata:
766
784
  end = self.github_context["event"]["pull_request"]["head"]["sha"]
767
785
  # Push event.
768
786
  else:
769
- start = self.github_context["event"]["before"]
787
+ start = self.github_context["event"].get("before")
770
788
  end = os.environ["GITHUB_SHA"]
771
789
  assert end
772
790
  logging.debug(f"Commit range: {start} -> {end}")
@@ -788,15 +806,41 @@ class Metadata:
788
806
  if not self.commit_range:
789
807
  return None
790
808
  start, end = self.commit_range
791
- # Remove the last commit, as the commit range is inclusive.
792
- return tuple(
793
- Repository(
794
- ".",
795
- from_commit=start,
796
- to_commit=end,
797
- order="reverse",
798
- ).traverse_commits(),
799
- )[:-1]
809
+
810
+ # Sanity check: make sure the start commit exists in the repository.
811
+ # XXX Even if we skip the start commit later on (because the range is
812
+ # inclusive), we still need to make sure it exists: PyDriller stills needs to
813
+ # find it to be able to traverse the commit history.
814
+ for commit_id in (start, end):
815
+ if not commit_id:
816
+ continue
817
+ try:
818
+ _ = self.git.get_commit(commit_id)
819
+ except ValueError:
820
+ logging.error(
821
+ f"Cannot find commit {commit_id} in repository. "
822
+ "Repository was probably not checked out with enough depth. "
823
+ f"Current depth is {self.git.total_commits()}. "
824
+ )
825
+ logging.warning(
826
+ "Skipping metadata extraction of the range of new commits."
827
+ )
828
+ return None
829
+
830
+ if not start:
831
+ logging.warning("No start commit found. Only one commit in range.")
832
+ assert end
833
+ return (self.git.get_commit(end),)
834
+
835
+ commit_list = []
836
+ for index, commit in enumerate(
837
+ Repository(".", from_commit=start, to_commit=end).traverse_commits()
838
+ ):
839
+ # Skip the first commit because the commit range is inclusive.
840
+ if index == 0:
841
+ continue
842
+ commit_list.append(commit)
843
+ return tuple(commit_list)
800
844
 
801
845
  @cached_property
802
846
  def new_commits_matrix(self) -> Matrix | None:
@@ -845,10 +889,27 @@ class Metadata:
845
889
  else None
846
890
  )
847
891
 
892
+ @cached_property
893
+ def mailmap_exists(self) -> bool:
894
+ return MAILMAP_PATH.is_file()
895
+
848
896
  @cached_property
849
897
  def gitignore_exists(self) -> bool:
850
898
  return GITIGNORE_PATH.is_file()
851
899
 
900
+ @cached_property
901
+ def gitignore_parser(self) -> Parser | None:
902
+ """Returns a parser for the ``.gitignore`` file, if it exists."""
903
+ if self.gitignore_exists:
904
+ logging.debug(f"Parse {GITIGNORE_PATH}")
905
+ return get_parser_from_file(GITIGNORE_PATH)
906
+ return None
907
+
908
+ def gitignore_match(self, file_path: Path | str) -> bool:
909
+ if self.gitignore_parser and self.gitignore_parser.match(file_path):
910
+ return True
911
+ return False
912
+
852
913
  def glob_files(self, *patterns: str) -> list[Path]:
853
914
  """Return all file path matching the ``patterns``.
854
915
 
@@ -878,12 +939,6 @@ class Metadata:
878
939
  current_dir = Path.cwd()
879
940
  seen = set()
880
941
 
881
- # If the .gitignore file exists, we parse it to filter out ignored files.
882
- gitignore = None
883
- if self.gitignore_exists:
884
- logging.debug(f"Load {GITIGNORE_PATH} to filter out ignored files.")
885
- gitignore = get_parser_from_file(GITIGNORE_PATH)
886
-
887
942
  for file_path in iglob(
888
943
  patterns,
889
944
  flags=NODIR | GLOBSTAR | DOTGLOB | GLOBTILDE | BRACE | FOLLOW | NEGATE,
@@ -913,7 +968,7 @@ class Metadata:
913
968
  continue
914
969
 
915
970
  # Skip files that are ignored by .gitignore.
916
- if gitignore and gitignore.match(file_path):
971
+ if self.gitignore_match(file_path):
917
972
  logging.debug(f"Skip file matching {GITIGNORE_PATH}: {file_path}")
918
973
  continue
919
974
 
@@ -934,6 +989,16 @@ class Metadata:
934
989
  "!**/package-lock.json",
935
990
  )
936
991
 
992
+ @cached_property
993
+ def yaml_files(self) -> list[Path]:
994
+ """Returns a list of YAML files."""
995
+ return self.glob_files("**/*.{yaml,yml}")
996
+
997
+ @cached_property
998
+ def workflow_files(self) -> list[Path]:
999
+ """Returns a list of GitHub workflow files."""
1000
+ return self.glob_files(".github/workflows/**/*.{yaml,yml}")
1001
+
937
1002
  @cached_property
938
1003
  def doc_files(self) -> list[Path]:
939
1004
  """Returns a list of doc files."""
@@ -946,6 +1011,15 @@ class Metadata:
946
1011
  """Returns a list of Markdown files."""
947
1012
  return self.glob_files("**/*.{markdown,mdown,mkdn,mdwn,mkd,md,mdtxt,mdtext}")
948
1013
 
1014
+ @cached_property
1015
+ def image_files(self) -> list[Path]:
1016
+ """Returns a list of image files.
1017
+
1018
+ Inspired by the list of image extensions supported by calibre's image-actions:
1019
+ https://github.com/calibreapp/image-actions/blob/f325757/src/constants.ts#L32
1020
+ """
1021
+ return self.glob_files("**/*.{jpeg,jpg,png,webp,avif}")
1022
+
949
1023
  @cached_property
950
1024
  def zsh_files(self) -> list[Path]:
951
1025
  """Returns a list of Zsh files."""
@@ -1194,8 +1268,8 @@ class Metadata:
1194
1268
  "os": [
1195
1269
  "ubuntu-24.04-arm",
1196
1270
  "ubuntu-24.04",
1197
- "macos-15",
1198
- "macos-13",
1271
+ "macos-26",
1272
+ "macos-15-intel",
1199
1273
  "windows-11-arm",
1200
1274
  "windows-2025",
1201
1275
  ],
@@ -1223,14 +1297,14 @@ class Metadata:
1223
1297
  },
1224
1298
  {
1225
1299
  "target": "macos-arm64",
1226
- "os": "macos-15",
1300
+ "os": "macos-26",
1227
1301
  "platform_id": "macos",
1228
1302
  "arch": "arm64",
1229
1303
  "extension": "bin",
1230
1304
  },
1231
1305
  {
1232
1306
  "target": "macos-x64",
1233
- "os": "macos-13",
1307
+ "os": "macos-15-intel",
1234
1308
  "platform_id": "macos",
1235
1309
  "arch": "x64",
1236
1310
  "extension": "bin",
@@ -1291,25 +1365,25 @@ class Metadata:
1291
1365
  "bin_name": "mpm-linux-x64-6f27db4.bin",
1292
1366
  },
1293
1367
  {
1294
- "os": "macos-15",
1368
+ "os": "macos-26",
1295
1369
  "entry_point": "mpm",
1296
1370
  "commit": "346ce664f055fbd042a25ee0b7e96702e95",
1297
1371
  "bin_name": "mpm-macos-arm64-346ce66.bin",
1298
1372
  },
1299
1373
  {
1300
- "os": "macos-15",
1374
+ "os": "macos-26",
1301
1375
  "entry_point": "mpm",
1302
1376
  "commit": "6f27db47612aaee06fdf08744b09a9f5f6c2",
1303
1377
  "bin_name": "mpm-macos-arm64-6f27db4.bin",
1304
1378
  },
1305
1379
  {
1306
- "os": "macos-13",
1380
+ "os": "macos-15-intel",
1307
1381
  "entry_point": "mpm",
1308
1382
  "commit": "346ce664f055fbd042a25ee0b7e96702e95",
1309
1383
  "bin_name": "mpm-macos-x64-346ce66.bin",
1310
1384
  },
1311
1385
  {
1312
- "os": "macos-13",
1386
+ "os": "macos-15-intel",
1313
1387
  "entry_point": "mpm",
1314
1388
  "commit": "6f27db47612aaee06fdf08744b09a9f5f6c2",
1315
1389
  "bin_name": "mpm-macos-x64-6f27db4.bin",
@@ -1510,11 +1584,15 @@ class Metadata:
1510
1584
  "is_bot": self.is_bot,
1511
1585
  "new_commits": self.new_commits_hash,
1512
1586
  "release_commits": self.release_commits_hash,
1587
+ "mailmap_exists": self.mailmap_exists,
1513
1588
  "gitignore_exists": self.gitignore_exists,
1514
1589
  "python_files": self.python_files,
1515
1590
  "json_files": self.json_files,
1591
+ "yaml_files": self.yaml_files,
1592
+ "workflow_files": self.workflow_files,
1516
1593
  "doc_files": self.doc_files,
1517
1594
  "markdown_files": self.markdown_files,
1595
+ "image_files": self.image_files,
1518
1596
  "zsh_files": self.zsh_files,
1519
1597
  "is_python_project": self.is_python_project,
1520
1598
  "package_name": self.package_name,
@@ -28,7 +28,11 @@ from typing import Generator, Sequence
28
28
  import yaml
29
29
  from boltons.iterutils import flatten
30
30
  from boltons.strutils import strip_ansi
31
- from click_extra.testing import args_cleanup, render_cli_run
31
+ from click_extra.testing import (
32
+ args_cleanup,
33
+ regex_fullmatch_line_by_line,
34
+ render_cli_run,
35
+ )
32
36
  from extra_platforms import Group, _TNestedReferences, current_os
33
37
 
34
38
 
@@ -265,9 +269,7 @@ class CLITestCase:
265
269
  raise AssertionError(f"{name} does not match regex {regex}")
266
270
 
267
271
  elif field_id.endswith("_regex_fullmatch"):
268
- regex = field_data
269
- if not regex.fullmatch(output):
270
- raise AssertionError(f"{name} does not fully match regex {regex}")
272
+ regex_fullmatch_line_by_line(field_data, output)
271
273
 
272
274
 
273
275
  DEFAULT_TEST_PLAN: list[CLITestCase] = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gha-utils
3
- Version: 4.20.0
3
+ Version: 4.22.0
4
4
  Summary: ⚙️ CLI helpers for GitHub Actions + reuseable workflows
5
5
  Author-email: Kevin Deldycke <kevin@deldycke.com>
6
6
  Project-URL: Homepage, https://github.com/kdeldycke/workflows
@@ -50,7 +50,7 @@ Requires-Python: >=3.11
50
50
  Description-Content-Type: text/markdown
51
51
  Requires-Dist: boltons>=24.0.0
52
52
  Requires-Dist: bump-my-version<1.1.1,>=0.32.2
53
- Requires-Dist: click-extra~=6.0.0
53
+ Requires-Dist: click-extra~=6.0.2
54
54
  Requires-Dist: extra-platforms~=3.2.0
55
55
  Requires-Dist: packaging~=25.0
56
56
  Requires-Dist: py-walk~=0.3.3
@@ -1,6 +1,6 @@
1
1
  boltons>=24.0.0
2
2
  bump-my-version<1.1.1,>=0.32.2
3
- click-extra~=6.0.0
3
+ click-extra~=6.0.2
4
4
  extra-platforms~=3.2.0
5
5
  packaging~=25.0
6
6
  py-walk~=0.3.3
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  # Docs: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
3
3
  name = "gha-utils"
4
- version = "4.20.0"
4
+ version = "4.22.0"
5
5
  # Python versions and their status: https://devguide.python.org/versions/
6
6
  requires-python = ">= 3.11"
7
7
  description = "⚙️ CLI helpers for GitHub Actions + reuseable workflows"
@@ -78,8 +78,8 @@ dependencies = [
78
78
  # v0.32.2 is the first fixing an issue preventing compilation with Nuitka.
79
79
  # v1.1.1 and later have some regressions: see https://github.com/callowayproject/bump-my-version/issues/331
80
80
  "bump-my-version >= 0.32.2, < 1.1.1",
81
- # Click Extra 6.0.0 fix an isue with --version option.
82
- "click-extra ~= 6.0.0",
81
+ # Click Extra 6.0.2 adds a new regex_fullmatch_line_by_line() function we rely on.
82
+ "click-extra ~= 6.0.2",
83
83
  "extra-platforms ~= 3.2.0",
84
84
  "packaging ~= 25.0",
85
85
  # In the future, replace py-walk with wcmatch once the latter supports gitignore files:
@@ -115,6 +115,8 @@ gha-utils = "gha_utils.__main__:main"
115
115
 
116
116
  [tool.uv]
117
117
  package = true
118
+ # 0.9.0 is the snapshot version we used when we migrated from requirements/uv.txt to pyproject.toml.
119
+ required-version = "~= 0.9.0"
118
120
 
119
121
  [tool.mypy]
120
122
  warn_unused_configs = true
@@ -144,7 +146,7 @@ addopts = [
144
146
  xfail_strict = true
145
147
 
146
148
  [tool.bumpversion]
147
- current_version = "4.20.0"
149
+ current_version = "4.22.0"
148
150
  allow_dirty = true
149
151
  ignore_missing_files = true
150
152
 
@@ -71,6 +71,16 @@ def iter_checks(metadata: Any, expected: Any, context: Any) -> None:
71
71
  assert isinstance(metadata, dict)
72
72
  assert set(metadata) == set(expected)
73
73
  for key, value in expected.items():
74
+ # By convention, keys ending with "_files" are path strings so they need to
75
+ # be adjusted for Windows.
76
+ if key.endswith("_files") and is_windows():
77
+ # Path are stored as a list in JSON format.
78
+ if isinstance(value, list):
79
+ value = [v.replace("/", "\\") for v in value]
80
+ # Path are space-separated and serialized as a string in GitHub format.
81
+ else:
82
+ value = value.replace("/", "\\")
83
+
74
84
  iter_checks(metadata[key], value, metadata)
75
85
 
76
86
  elif isinstance(expected, list):
@@ -90,24 +100,9 @@ expected = {
90
100
  "is_bot": False,
91
101
  "new_commits": None,
92
102
  "release_commits": None,
103
+ "mailmap_exists": True,
93
104
  "gitignore_exists": True,
94
105
  "python_files": [
95
- "gha_utils\\__init__.py",
96
- "gha_utils\\__main__.py",
97
- "gha_utils\\changelog.py",
98
- "gha_utils\\cli.py",
99
- "gha_utils\\mailmap.py",
100
- "gha_utils\\matrix.py",
101
- "gha_utils\\metadata.py",
102
- "gha_utils\\test_plan.py",
103
- "tests\\__init__.py",
104
- "tests\\test_changelog.py",
105
- "tests\\test_mailmap.py",
106
- "tests\\test_matrix.py",
107
- "tests\\test_metadata.py",
108
- ]
109
- if is_windows()
110
- else [
111
106
  "gha_utils/__init__.py",
112
107
  "gha_utils/__main__.py",
113
108
  "gha_utils/changelog.py",
@@ -123,28 +118,54 @@ expected = {
123
118
  "tests/test_metadata.py",
124
119
  ],
125
120
  "json_files": [],
121
+ "yaml_files": [
122
+ ".github/dependabot.yaml",
123
+ ".github/funding.yml",
124
+ ".github/labeller-content-based.yaml",
125
+ ".github/labeller-file-based.yaml",
126
+ ".github/labels-awesome.yaml",
127
+ ".github/labels.yaml",
128
+ ".github/workflows/autofix.yaml",
129
+ ".github/workflows/autolock.yaml",
130
+ ".github/workflows/changelog.yaml",
131
+ ".github/workflows/debug.yaml",
132
+ ".github/workflows/docs.yaml",
133
+ ".github/workflows/label-sponsors.yaml",
134
+ ".github/workflows/labeller-content-based.yaml",
135
+ ".github/workflows/labeller-file-based.yaml",
136
+ ".github/workflows/labels.yaml",
137
+ ".github/workflows/lint.yaml",
138
+ ".github/workflows/release.yaml",
139
+ ".github/workflows/tests.yaml",
140
+ "tests/cli-test-plan.yaml",
141
+ ],
142
+ "workflow_files": [
143
+ ".github/workflows/autofix.yaml",
144
+ ".github/workflows/autolock.yaml",
145
+ ".github/workflows/changelog.yaml",
146
+ ".github/workflows/debug.yaml",
147
+ ".github/workflows/docs.yaml",
148
+ ".github/workflows/label-sponsors.yaml",
149
+ ".github/workflows/labeller-content-based.yaml",
150
+ ".github/workflows/labeller-file-based.yaml",
151
+ ".github/workflows/labels.yaml",
152
+ ".github/workflows/lint.yaml",
153
+ ".github/workflows/release.yaml",
154
+ ".github/workflows/tests.yaml",
155
+ ],
126
156
  "doc_files": [
127
- ".github\\code-of-conduct.md",
128
- "changelog.md",
129
- "readme.md",
130
- ]
131
- if is_windows()
132
- else [
133
157
  ".github/code-of-conduct.md",
134
158
  "changelog.md",
135
159
  "readme.md",
136
160
  ],
137
161
  "markdown_files": [
138
- ".github\\code-of-conduct.md",
139
- "changelog.md",
140
- "readme.md",
141
- ]
142
- if is_windows()
143
- else [
144
162
  ".github/code-of-conduct.md",
145
163
  "changelog.md",
146
164
  "readme.md",
147
165
  ],
166
+ "image_files": [
167
+ "docs/assets/repo-workflow-permissions.png",
168
+ ],
148
169
  "zsh_files": [],
149
170
  "is_python_project": True,
150
171
  "package_name": "gha-utils",
@@ -184,14 +205,14 @@ expected = {
184
205
  },
185
206
  {
186
207
  "target": "macos-arm64",
187
- "os": "macos-15",
208
+ "os": "macos-26",
188
209
  "platform_id": "macos",
189
210
  "arch": "arm64",
190
211
  "extension": "bin",
191
212
  },
192
213
  {
193
214
  "target": "macos-x64",
194
- "os": "macos-13",
215
+ "os": "macos-15-intel",
195
216
  "platform_id": "macos",
196
217
  "arch": "x64",
197
218
  "extension": "bin",
@@ -215,8 +236,8 @@ expected = {
215
236
  "os": [
216
237
  "ubuntu-24.04-arm",
217
238
  "ubuntu-24.04",
218
- "macos-15",
219
- "macos-13",
239
+ "macos-26",
240
+ "macos-15-intel",
220
241
  "windows-11-arm",
221
242
  "windows-2025",
222
243
  ],
@@ -239,14 +260,14 @@ expected = {
239
260
  },
240
261
  {
241
262
  "target": "macos-arm64",
242
- "os": "macos-15",
263
+ "os": "macos-26",
243
264
  "platform_id": "macos",
244
265
  "arch": "arm64",
245
266
  "extension": "bin",
246
267
  },
247
268
  {
248
269
  "target": "macos-x64",
249
- "os": "macos-13",
270
+ "os": "macos-15-intel",
250
271
  "platform_id": "macos",
251
272
  "arch": "x64",
252
273
  "extension": "bin",
@@ -290,13 +311,13 @@ expected = {
290
311
  "bin_name": regex(r"gha-utils-linux-x64-[a-z0-9]+\.bin"),
291
312
  },
292
313
  {
293
- "os": "macos-15",
314
+ "os": "macos-26",
294
315
  "entry_point": "gha-utils",
295
316
  "commit": regex(r"[a-z0-9]+"),
296
317
  "bin_name": regex(r"gha-utils-macos-arm64-[a-z0-9]+\.bin"),
297
318
  },
298
319
  {
299
- "os": "macos-13",
320
+ "os": "macos-15-intel",
300
321
  "entry_point": "gha-utils",
301
322
  "commit": regex(r"[a-z0-9]+"),
302
323
  "bin_name": regex(r"gha-utils-macos-x64-[a-z0-9]+\.bin"),
File without changes
File without changes