diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 7de3fdd1..30834e59 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -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' }} diff --git a/mypy-requirements.txt b/mypy-requirements.txt index 11c21daa..0339757d 100644 --- a/mypy-requirements.txt +++ b/mypy-requirements.txt @@ -5,6 +5,6 @@ pyfakefs pytest pytest-mock python-gnupg # untyped :( +requests>=2.34 sigstore==4.4.0 types-paramiko -types-requests diff --git a/pyproject.toml b/pyproject.toml index 9099a69e..1273591b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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$", ] diff --git a/requirements.in b/requirements.in index 374e9ba2..9a294de7 100644 --- a/requirements.in +++ b/requirements.in @@ -5,4 +5,5 @@ alive_progress>=3.3.0 python-gnupg aiohttp blurb>=1.2.1 +requests>=2.34 sigstore>=4.4.0 diff --git a/requirements.txt b/requirements.txt index 0179e4f2..824d8b30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 \ diff --git a/tests/test_release_tag.py b/tests/test_release_tag.py index a1947c3c..8fe590af 100644 --- a/tests/test_release_tag.py +++ b/tests/test_release_tag.py @@ -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 @@ -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""" diff --git a/tests/test_run_release.py b/tests/test_run_release.py index d539f78c..35008a59 100644 --- a/tests/test_run_release.py +++ b/tests/test_run_release.py @@ -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 @@ -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) @@ -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" ): @@ -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, ) @@ -116,14 +119,17 @@ 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" @@ -131,8 +137,8 @@ def fake_check_output(cmd, **kwargs): 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) @@ -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) @@ -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, "
New in 3.13.0rc1 (unreleased)
", @@ -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, "
New in 3.13.0rc1 (unreleased)
", diff --git a/tests/test_sbom.py b/tests/test_sbom.py index adae5975..10878984 100644 --- a/tests/test_sbom.py +++ b/tests/test_sbom.py @@ -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 @@ -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") @@ -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}", @@ -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"]], @@ -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() @@ -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( @@ -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( diff --git a/windows-release/purge.py b/windows-release/purge.py index b62e3eb2..7abd5515 100644 --- a/windows-release/purge.py +++ b/windows-release/purge.py @@ -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)