Skip to content
Open
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
3 changes: 2 additions & 1 deletion .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ name: "Build release artifacts"

permissions: {}

# Set from inputs for workflow_dispatch, or set defaults to test push/PR events
env:
FORCE_COLOR: 1
# Set from inputs for workflow_dispatch, or set defaults to test push/PR events
GIT_REMOTE: ${{ github.event.inputs.git_remote || 'python' }}
GIT_COMMIT: ${{ github.event.inputs.git_commit || '55ea59e7dc35e1363b203ae4dd9cfc3a0ac0a844' }}
CPYTHON_RELEASE: ${{ github.event.inputs.cpython_release || '3.15.0a8' }}
Expand Down
2 changes: 1 addition & 1 deletion mypy-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ pyfakefs
pytest
pytest-mock
python-gnupg # untyped :(
requests>=2.34
sigstore==4.4.0
types-paramiko
types-requests
4 changes: 0 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,5 @@ extra_checks = true
warn_unreachable = true

exclude = [
"^tests/test_release_tag.py$",
"^tests/test_run_release.py$",
"^tests/test_sbom.py$",
"^windows-release/merge-and-upload.py$",
"^windows-release/purge.py$",
]
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ alive_progress>=3.3.0
python-gnupg
aiohttp
blurb>=1.2.1
requests>=2.34
sigstore>=4.4.0
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -890,9 +890,10 @@ pyopenssl==26.0.0 \
python-gnupg==0.5.6 \
--hash=sha256:b5050a55663d8ab9fcc8d97556d229af337a87a3ebebd7054cbd8b7e2043394a
# via -r requirements.in
requests==2.33.0 \
--hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b
requests==2.34.2 \
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0
# via
# -r requirements.in
# id
# sigstore
rfc3161-client==1.0.6 \
Expand Down
7 changes: 3 additions & 4 deletions tests/test_release_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,14 @@ def test_tag_committed_at_not_found() -> None:

# Act / Assert
with pytest.raises(SystemExit):
tag.committed_at()
tag.committed_at


def test_tag_committed(mocker: MockerFixture) -> None:
# Arrange
tag = release.Tag("3.12.2")

proc = CompletedProcess([], 0)
proc.stdout = b"1707250784"
proc = CompletedProcess([], 0, stdout=b"1707250784")
mocker.patch("subprocess.run", return_value=proc)

# Act / Assert
Expand Down Expand Up @@ -156,7 +155,7 @@ def test_tag_long_name() -> None:
],
)
def test_tag_is_security_release(
version: str, expected: str, mocker: MockerFixture
version: str, expected: bool, mocker: MockerFixture
) -> None:
# Arrange
mock_response = b"""
Expand Down
38 changes: 24 additions & 14 deletions tests/test_run_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
import contextlib
import io
import tarfile
from collections.abc import Iterator
from contextlib import nullcontext as does_not_raise
from pathlib import Path
from typing import cast
from typing import Any, cast

import pytest

Expand All @@ -17,7 +18,7 @@
"version",
["sigstore 4.0.0", "sigstore 4.1.0"],
)
def test_check_sigstore_version_success(version) -> None:
def test_check_sigstore_version_success(version: str) -> None:
# Verify runs with no exceptions
run_release.check_sigstore_version(version)

Expand All @@ -26,7 +27,7 @@ def test_check_sigstore_version_success(version) -> None:
"version",
["sigstore 3.4.0", "sigstore 3.6.2", "sigstore 3.6.6", ""],
)
def test_check_sigstore_version_exception(version) -> None:
def test_check_sigstore_version_exception(version: str) -> None:
with pytest.raises(
ReleaseException, match="Sigstore version not detected or not valid"
):
Expand Down Expand Up @@ -89,13 +90,15 @@ def test_invalid_extract_github_owner() -> None:
],
)
def test_check_cpython_repo_branch(
monkeypatch, release_tag: str, git_current_branch: str, expectation
monkeypatch: pytest.MonkeyPatch,
release_tag: str,
git_current_branch: str,
expectation: contextlib.AbstractContextManager[object],
) -> None:
# Arrange
db = {"release": Tag(release_tag), "git_repo": "/fake/repo"}
monkeypatch.setattr(
run_release.subprocess,
"check_output",
"run_release.subprocess.check_output",
lambda *args, **kwargs: git_current_branch,
)

Expand All @@ -116,23 +119,26 @@ def test_check_cpython_repo_branch(
],
)
def test_check_cpython_repo_age(
monkeypatch, age_seconds: int, user_continues: bool | None, expectation
monkeypatch: pytest.MonkeyPatch,
age_seconds: int,
user_continues: bool | None,
expectation: contextlib.AbstractContextManager[object],
) -> None:
# Arrange
db = {"release": Tag("3.15.0a6"), "git_repo": "/fake/repo"}
current_time = 1700000000
commit_timestamp = current_time - age_seconds

def fake_check_output(cmd, **kwargs):
def fake_check_output(cmd: list[str], **kwargs: Any) -> str:
cmd_str = " ".join(cmd)
if "%ct" in cmd_str:
return f"{commit_timestamp}\n"
if "%cr" in cmd_str:
return "some time ago\n"
return ""

monkeypatch.setattr(run_release.subprocess, "check_output", fake_check_output)
monkeypatch.setattr(run_release.time, "time", lambda: current_time)
monkeypatch.setattr("run_release.subprocess.check_output", fake_check_output)
monkeypatch.setattr("run_release.time.time", lambda: current_time)
if user_continues is not None:
monkeypatch.setattr(run_release, "ask_question", lambda _: user_continues)

Expand Down Expand Up @@ -161,12 +167,12 @@ def prepare_fake_docs(tmp_path: Path, content: str) -> None:


@contextlib.contextmanager
def fake_answers(monkeypatch: pytest.MonkeyPatch, answers: list[str]) -> None:
def fake_answers(monkeypatch: pytest.MonkeyPatch, answers: list[str]) -> Iterator[None]:
"""Monkey-patch input() to give the given answers. All must be consumed."""

answers_left = list(answers)

def fake_input(question):
def fake_input(question: str) -> str:
print(question, "--", answers_left[0])
return answers_left.pop(0)

Expand Down Expand Up @@ -207,7 +213,9 @@ def test_check_doc_unreleased_version_ok(tmp_path: Path) -> None:
run_release.check_doc_unreleased_version(cast(ReleaseShelf, db))


def test_check_doc_unreleased_version_not_ok(monkeypatch, tmp_path: Path) -> None:
def test_check_doc_unreleased_version_not_ok(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
prepare_fake_docs(
tmp_path,
"<div>New in 3.13.0rc1 (unreleased)</div>",
Expand All @@ -220,7 +228,9 @@ def test_check_doc_unreleased_version_not_ok(monkeypatch, tmp_path: Path) -> Non
run_release.check_doc_unreleased_version(cast(ReleaseShelf, db))


def test_check_doc_unreleased_version_waived(monkeypatch, tmp_path: Path) -> None:
def test_check_doc_unreleased_version_waived(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
prepare_fake_docs(
tmp_path,
"<div>New in 3.13.0rc1 (unreleased)</div>",
Expand Down
26 changes: 15 additions & 11 deletions tests/test_sbom.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import re
import unittest.mock
from pathlib import Path
from typing import Any

import pytest
from pytest_mock import MockerFixture

import sbom

Expand All @@ -27,7 +29,7 @@ def test_spdx_id(value: str, expected: str) -> None:
assert sbom.spdx_id(value) == expected


def test_spdx_id_collisions():
def test_spdx_id_collisions() -> None:
sbom._SPDX_IDS_TO_VALUES = {} # Reset the cache.
assert (
sbom.spdx_id("SPDXRef-FILE-Lib/collections.py")
Expand All @@ -53,11 +55,13 @@ def test_spdx_id_collisions():
),
],
)
def test_calculate_package_verification_code(package_sha1s, package_verification_code):
def test_calculate_package_verification_code(
package_sha1s: list[str], package_verification_code: str
) -> None:
# Randomize because PackageVerificationCode is deterministic.
random.shuffle(package_sha1s)

input_sbom = {
input_sbom: Any = {
"files": [
{
"SPDXID": f"SPDXRef-FILE-{package_sha1}",
Expand All @@ -83,11 +87,11 @@ def test_calculate_package_verification_code(package_sha1s, package_verification
}


def test_normalization():
def test_normalization() -> None:
# Test that arbitrary JSON data can be normalized.
# Normalization doesn't have to make too much sense,
# only needs to be reproducible.
data = {
data: Any = {
"a": [1, 2, 3, {"b": [4, "c", [7, True, "2", {}]]}],
# This line tests that inner structures are sorted first.
"b": [[1, 2, "b"], [2, 1, "a"]],
Expand All @@ -99,7 +103,7 @@ def test_normalization():
}


def test_fetch_project_metadata_from_pypi(mocker):
def test_fetch_project_metadata_from_pypi(mocker: MockerFixture) -> None:
mock_urlopen = mocker.patch("sbom.urlopen")
mock_urlopen.return_value = unittest.mock.Mock()

Expand Down Expand Up @@ -173,8 +177,8 @@ def test_remove_pip_from_sbom() -> None:
assert sbom_data == expected


def test_create_cpython_sbom():
sbom_data = {"packages": []}
def test_create_cpython_sbom() -> None:
sbom_data: Any = {"packages": []}

artifact_path = str(pathlib.Path(__file__).parent / "fake-artifact.txt")
sbom.create_cpython_sbom(
Expand Down Expand Up @@ -240,9 +244,9 @@ def test_create_cpython_sbom():
],
)
def test_create_cpython_sbom_pre_release_download_location(
cpython_version, download_location
):
sbom_data = {"packages": []}
cpython_version: str, download_location: str
) -> None:
sbom_data: Any = {"packages": []}

artifact_path = str(pathlib.Path(__file__).parent / "fake-artifact.txt")
sbom.create_cpython_sbom(
Expand Down
2 changes: 1 addition & 1 deletion windows-release/purge.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,6 @@
print("Purged:")
for n in PATHS:
u = URL + n
with urlopen(Request(u, method="PURGE", headers={"Fastly-Soft-Purge": 1})) as r:
with urlopen(Request(u, method="PURGE", headers={"Fastly-Soft-Purge": "1"})) as r:
r.read()
print(" ", u)
Loading