Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions release.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,7 @@
import datetime
import glob
import hashlib
import json
import optparse
import os
import re
Expand All@@ -31,6 +32,7 @@
Self,
overload,
)
from urllib.request import urlopen
Comment thread
AA-Turner marked this conversation as resolved.

COMMASPACE = ", "
SPACE = " "
Expand DownExpand Up@@ -68,6 +70,11 @@ def get(
@overload
def get(self, key: Literal["sign_gpg"], default: bool | None = None) -> bool: ...

@overload
def get(
self, key: Literal["security_release"], default: bool | None = None
) -> bool: ...

@overload
def get(self, key: Literal["release"], default: Tag | None = None) -> Tag: ...

Expand DownExpand Up@@ -95,6 +102,9 @@ def __getitem__(self, key: Literal["ssh_key"]) -> str | None: ...
@overload
def __getitem__(self, key: Literal["sign_gpg"]) -> bool: ...

@overload
def __getitem__(self, key: Literal["security_release"]) -> bool: ...

@overload
def __getitem__(self, key: Literal["release"]) -> Tag: ...

Expand DownExpand Up@@ -124,6 +134,9 @@ def __setitem__(self, key: Literal["ssh_key"], value: str | None) -> None: ...
@overload
def __setitem__(self, key: Literal["sign_gpg"], value: bool) -> None: ...

@overload
def __setitem__(self, key: Literal["security_release"], value: bool) -> None: ...

@overload
def __setitem__(self, key: Literal["release"], value: Tag) -> None: ...

Expand DownExpand Up@@ -193,6 +206,13 @@ def is_release_candidate(self) -> bool:
def is_feature_freeze_release(self) -> bool:
return self.level == "b" and self.serial == 1

@property
def is_security_release(self) -> bool:
Comment thread
AA-Turner marked this conversation as resolved.
url = "https://peps.python.org/api/release-cycle.json"
with urlopen(url) as response:
data = json.loads(response.read())
return str(data[self.basic_version]["status"]) == "security"

@property
def nickname(self) -> str:
return self.text.replace(".", "")
Expand Down
31 changes: 22 additions & 9 deletions run_release.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -248,9 +248,10 @@ def __init__(
self.db["ssh_key"] = ssh_key
if not self.db.get("sign_gpg"):
self.db["sign_gpg"] = sign_gpg

if not self.db.get("release"):
self.db["release"] = release_tag
if not self.db.get("security_release"):
self.db["security_release"] = self.db["release"].is_security_release

print("Release data: ")
print(f"- Branch: {release_tag.branch}")
Expand All@@ -260,6 +261,7 @@ def __init__(
print(f"- SSH username: {self.db['ssh_user']}")
print(f"- SSH key: {self.db['ssh_key'] or 'Default'}")
print(f"- Sign with GPG: {self.db['sign_gpg']}")
print(f"- Security release: {self.db['security_release']}")
print()

def checkpoint(self) -> None:
Expand DownExpand Up@@ -1000,18 +1002,29 @@ def wait_until_all_files_are_in_folder(db: ReleaseShelf) -> None:
are_windows_files_there = f"python-{release}.exe" in all_files
are_macos_files_there = f"python-{release}-macos11.pkg" in all_files
are_linux_files_there = f"Python-{release}.tgz" in all_files
are_all_files_there = (
are_linux_files_there and are_windows_files_there and are_macos_files_there
)

if db["security_release"]:
# For security releases, only check Linux files
are_all_files_there = are_linux_files_there
else:
# For regular releases, check all platforms
are_all_files_there = (
are_linux_files_there
and are_windows_files_there
and are_macos_files_there
)

if not are_all_files_there:
linux_tick = "✅" if are_linux_files_there else "❌"
windows_tick = "✅" if are_windows_files_there else "❌"
macos_tick = "✅" if are_macos_files_there else "❌"
print(
f"\rWaiting for files: Linux {linux_tick} Windows {windows_tick} Mac {macos_tick} ",
flush=True,
end="",
)

if db["security_release"]:
waiting = f"\rWaiting for files: Linux {linux_tick} (security release - only checking Linux)"
else:
waiting = f"\rWaiting for files: Linux {linux_tick} Windows {windows_tick} Mac {macos_tick} "

print(waiting, flush=True, end="")
time.sleep(1)
print()

Expand Down
31 changes: 31 additions & 0 deletions tests/test_release_tag.py
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
import io
from subprocess import CompletedProcess

import pytest
Expand DownExpand Up@@ -140,3 +141,33 @@ def test_tag_long_name() -> None:
assert rc.long_name == "3.13.0 release candidate 3"
assert final_zero.long_name == "3.13.0"
assert final_3.long_name == "3.13.3"


@pytest.mark.parametrize(
["version", "expected"],
[
("3.12.10", True),
("3.13.3", False),
],
)
def test_tag_is_security_release(
version: str, expected: str, mocker: MockerFixture
) -> None:
# Arrange
mock_response = b"""
{
"3.13": {
"status": "bugfix"
},
"3.12": {
"status": "security"
}
}
"""
mocker.patch("urllib.request.urlopen", return_value=io.BytesIO(mock_response))

# Act
tag = release.Tag(version)

# Assert
assert tag.is_security_release is expected