Skip to content

Commit b7780ba

Browse files
authored
feat(toxgen): Remove timestamp prone to merge conflicts (#4860)
### Description Most of the merge conflicts concerning `tox.ini` come from the timestamp of the last update. If two PRs regenerate `tox.ini`, they'll be guaranteed to have a merge conflict that needs to be resolved by hand. This is time consuming and unnecessary. We still need some way of figuring out when the file was last updated. This is important for checking unwanted edits to `tox.ini` (instead of to the template it's generated from). In order to check for them, we need to regenerate the file on the current branch and compare it to the committed version. However, if there have been new package versions in the meantime, these would then pop up in the diff. One solution is to regenerate the file ignoring any new releases after the last update, which is what we were using the last updated timestamp for. In this PR, we simply figure out the timestamp from `git` history, removing the need for storing it explicitly anywhere. #### Issues <!-- * resolves: #1234 * resolves: LIN-1234 --> #### Reminders - Please add tests to validate your changes, and lint your code using `tox -e linters`. - Add GH Issue ID _&_ Linear ID (if applicable) - PR title should use [conventional commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type) style (`feat:`, `fix:`, `ref:`, `meta:`) - For external contributors: [CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md), [Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord community](https://discord.gg/Ww9hbqr)
1 parent 4e1b96c commit b7780ba

File tree

5 files changed

+27
-34
lines changed

5 files changed

+27
-34
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ jobs:
4040

4141
steps:
4242
- uses: actions/[email protected]
43+
with:
44+
ref: ${{ github.event.pull_request.head.sha }}
45+
fetch-depth: 0
4346
- uses: actions/setup-python@v6
4447
with:
4548
python-version: 3.12

scripts/populate_tox/populate_tox.py

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import hashlib
99
import json
1010
import os
11+
import subprocess
1112
import sys
1213
import time
1314
from bisect import bisect_left
@@ -509,9 +510,7 @@ def _render_dependencies(integration: str, releases: list[Version]) -> list[str]
509510
return rendered
510511

511512

512-
def write_tox_file(
513-
packages: dict, update_timestamp: bool, last_updated: datetime
514-
) -> None:
513+
def write_tox_file(packages: dict) -> None:
515514
template = ENV.get_template("tox.jinja")
516515

517516
context = {"groups": {}}
@@ -530,11 +529,6 @@ def write_tox_file(
530529
}
531530
)
532531

533-
if update_timestamp:
534-
context["updated"] = datetime.now(tz=timezone.utc).isoformat()
535-
else:
536-
context["updated"] = last_updated.isoformat()
537-
538532
rendered = template.render(context)
539533

540534
with open(TOX_FILE, "w") as file:
@@ -623,19 +617,21 @@ def get_file_hash() -> str:
623617

624618

625619
def get_last_updated() -> Optional[datetime]:
626-
timestamp = None
627-
628-
with open(TOX_FILE, "r") as f:
629-
for line in f:
630-
if line.startswith("# Last generated:"):
631-
timestamp = datetime.fromisoformat(line.strip().split()[-1])
632-
break
633-
634-
if timestamp is None:
635-
print(
636-
"Failed to find out when tox.ini was last generated; the timestamp seems to be missing from the file."
637-
)
638-
620+
repo_root = subprocess.run(
621+
["git", "rev-parse", "--show-toplevel"],
622+
capture_output=True,
623+
text=True,
624+
).stdout.strip()
625+
tox_ini_path = Path(repo_root) / "tox.ini"
626+
627+
timestamp = subprocess.run(
628+
["git", "log", "-1", "--pretty=%ct", str(tox_ini_path)],
629+
capture_output=True,
630+
text=True,
631+
).stdout.strip()
632+
633+
timestamp = datetime.fromtimestamp(int(timestamp), timezone.utc)
634+
print(f"Last committed tox.ini update: {timestamp}")
639635
return timestamp
640636

641637

@@ -675,7 +671,7 @@ def main(fail_on_changes: bool = False) -> None:
675671
print(f"Running in {'fail_on_changes' if fail_on_changes else 'normal'} mode.")
676672
last_updated = get_last_updated()
677673
if fail_on_changes:
678-
# We need to make the script ignore any new releases after the `last_updated`
674+
# We need to make the script ignore any new releases after the last updated
679675
# timestamp so that we don't fail CI on a PR just because a new package
680676
# version was released, leading to unrelated changes in tox.ini.
681677
print(
@@ -769,9 +765,7 @@ def main(fail_on_changes: bool = False) -> None:
769765
if fail_on_changes:
770766
old_file_hash = get_file_hash()
771767

772-
write_tox_file(
773-
packages, update_timestamp=not fail_on_changes, last_updated=last_updated
774-
)
768+
write_tox_file(packages)
775769

776770
# Sort the release cache file
777771
releases = []

scripts/populate_tox/releases.jsonl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
{"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Internet", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: HTTP Servers", "Topic :: Software Development", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Application Frameworks", "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed"], "name": "litestar", "requires_python": "<4.0,>=3.8", "version": "2.6.4", "yanked": false}}
108108
{"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: System :: Logging"], "name": "loguru", "requires_python": "<4.0,>=3.5", "version": "0.7.3", "yanked": false}}
109109
{"info": {"classifiers": ["Intended Audience :: Developers", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed"], "name": "openai", "requires_python": ">=3.7.1", "version": "1.0.1", "yanked": false}}
110-
{"info": {"classifiers": ["Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed"], "name": "openai", "requires_python": ">=3.8", "version": "1.109.0", "yanked": false}}
110+
{"info": {"classifiers": ["Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed"], "name": "openai", "requires_python": ">=3.8", "version": "1.109.1", "yanked": false}}
111111
{"info": {"classifiers": ["Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed"], "name": "openai", "requires_python": ">=3.7.1", "version": "1.37.2", "yanked": false}}
112112
{"info": {"classifiers": ["Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed"], "name": "openai", "requires_python": ">=3.8", "version": "1.73.0", "yanked": false}}
113113
{"info": {"classifiers": ["Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed"], "name": "openai-agents", "requires_python": ">=3.9", "version": "0.0.19", "yanked": false}}

scripts/populate_tox/tox.jinja

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
# or in the script (if you want to change the auto-generated part).
1010
# The file (and all resulting CI YAMLs) then needs to be regenerated via
1111
# "scripts/generate-test-files.sh".
12-
#
13-
# Last generated: {{ updated }}
1412

1513
[tox]
1614
requires =

tox.ini

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
# or in the script (if you want to change the auto-generated part).
1010
# The file (and all resulting CI YAMLs) then needs to be regenerated via
1111
# "scripts/generate-test-files.sh".
12-
#
13-
# Last generated: 2025-09-24T10:52:00.519014+00:00
1412

1513
[tox]
1614
requires =
@@ -68,12 +66,12 @@ envlist =
6866
{py3.8,py3.11,py3.12}-openai-base-v1.0.1
6967
{py3.8,py3.11,py3.12}-openai-base-v1.37.2
7068
{py3.8,py3.11,py3.12}-openai-base-v1.73.0
71-
{py3.8,py3.12,py3.13}-openai-base-v1.109.0
69+
{py3.8,py3.12,py3.13}-openai-base-v1.109.1
7270

7371
{py3.8,py3.11,py3.12}-openai-notiktoken-v1.0.1
7472
{py3.8,py3.11,py3.12}-openai-notiktoken-v1.37.2
7573
{py3.8,py3.11,py3.12}-openai-notiktoken-v1.73.0
76-
{py3.8,py3.12,py3.13}-openai-notiktoken-v1.109.0
74+
{py3.8,py3.12,py3.13}-openai-notiktoken-v1.109.1
7775

7876
{py3.9,py3.12,py3.13}-langgraph-v0.6.7
7977
{py3.10,py3.12,py3.13}-langgraph-v1.0.0a3
@@ -364,7 +362,7 @@ deps =
364362
openai-base-v1.0.1: openai==1.0.1
365363
openai-base-v1.37.2: openai==1.37.2
366364
openai-base-v1.73.0: openai==1.73.0
367-
openai-base-v1.109.0: openai==1.109.0
365+
openai-base-v1.109.1: openai==1.109.1
368366
openai-base: pytest-asyncio
369367
openai-base: tiktoken
370368
openai-base-v1.0.1: httpx<0.28
@@ -373,7 +371,7 @@ deps =
373371
openai-notiktoken-v1.0.1: openai==1.0.1
374372
openai-notiktoken-v1.37.2: openai==1.37.2
375373
openai-notiktoken-v1.73.0: openai==1.73.0
376-
openai-notiktoken-v1.109.0: openai==1.109.0
374+
openai-notiktoken-v1.109.1: openai==1.109.1
377375
openai-notiktoken: pytest-asyncio
378376
openai-notiktoken-v1.0.1: httpx<0.28
379377
openai-notiktoken-v1.37.2: httpx<0.28

0 commit comments

Comments
 (0)