Compare commits
6 Commits
default-sy
...
main
Author | SHA1 | Date | |
---|---|---|---|
75c37b4aa7 | |||
ef7160fe7c | |||
a6c839a31e | |||
ec401c7d6a | |||
7a5bed0454 | |||
d639b868a1 |
@ -6,6 +6,7 @@ PYTHON_VERSIONS = [
|
||||
"3.9",
|
||||
"3.10",
|
||||
"3.11",
|
||||
"3.12",
|
||||
"latest",
|
||||
]
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
---
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
rev: 24.4.2
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
rev: v4.6.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-merge-conflict
|
||||
@ -14,12 +14,12 @@ repos:
|
||||
- id: trailing-whitespace
|
||||
- id: name-tests-test
|
||||
exclude: tests/(common.py|util.py|(helpers|integration/factories)/(.+).py)
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v3.0.1
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
- id: isort
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.942
|
||||
rev: v1.10.0
|
||||
hooks:
|
||||
- id: mypy
|
||||
exclude: docs/
|
||||
|
@ -13,6 +13,8 @@
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
project = "release-gitter"
|
||||
copyright = "2021, iamthefij"
|
||||
author = "iamthefij"
|
||||
|
@ -2,6 +2,7 @@
|
||||
This builder functions as a pseudo builder that instead downloads and installs a binary file using
|
||||
release-gitter based on a pyproject.toml file. It's a total hack...
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
@ -16,7 +17,6 @@ from wheel.wheelfile import WheelFile
|
||||
import release_gitter as rg
|
||||
from release_gitter import removeprefix
|
||||
|
||||
|
||||
PACKAGE_NAME = "pseudo"
|
||||
|
||||
|
||||
|
@ -53,7 +53,7 @@ run = [
|
||||
]
|
||||
|
||||
[[tool.hatch.envs.test.matrix]]
|
||||
python = ["3", "3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||
python = ["3", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||
|
||||
[tool.hatch.envs.lint]
|
||||
detached = true
|
||||
@ -62,3 +62,8 @@ dependencies = ["pre-commit"]
|
||||
[tool.hatch.envs.lint.scripts]
|
||||
all = "pre-commit run --all-files"
|
||||
install-hooks = "pre-commit install --install-hooks"
|
||||
|
||||
[tool.isort]
|
||||
add_imports = ["from __future__ import annotations"]
|
||||
force_single_line = true
|
||||
profile = "black"
|
||||
|
@ -6,6 +6,7 @@ import platform
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass
|
||||
from io import BytesIO
|
||||
from itertools import product
|
||||
from mimetypes import guess_type
|
||||
from pathlib import Path
|
||||
from subprocess import check_call
|
||||
@ -18,7 +19,7 @@ from zipfile import ZipFile
|
||||
|
||||
import requests
|
||||
|
||||
__version__ = "2.2.1"
|
||||
__version__ = "2.3.0"
|
||||
|
||||
|
||||
class UnsupportedContentTypeError(ValueError):
|
||||
@ -47,6 +48,31 @@ def removesuffix(s: str, suf: str) -> str:
|
||||
return s[: -len(suf)] if s and s.endswith(suf) else s
|
||||
|
||||
|
||||
SYSTEM_SYNONYMS: list[list[str]] = [
|
||||
["Darwin", "darwin", "MacOS", "macos", "macOS"],
|
||||
["Windows", "windows", "win", "win32", "win64"],
|
||||
["Linux", "linux"],
|
||||
]
|
||||
|
||||
ARCH_SYNONYMS: list[list[str]] = [
|
||||
["arm"],
|
||||
["x86_64", "amd64", "AMD64"],
|
||||
["arm64", "aarch64", "armv8b", "armv8l"],
|
||||
["x86", "i386", "i686"],
|
||||
]
|
||||
|
||||
|
||||
def get_synonyms(value: str, thesaurus: list[list[str]]) -> list[str]:
|
||||
"""Gets synonym list for a given value."""
|
||||
results = [value]
|
||||
|
||||
for l in thesaurus:
|
||||
if value in l:
|
||||
results += l
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@dataclass
|
||||
class GitRemoteInfo:
|
||||
"""Extracts information about a repository"""
|
||||
@ -245,21 +271,29 @@ def match_asset(
|
||||
|
||||
system = platform.system()
|
||||
if system_mapping:
|
||||
system = system_mapping.get(system, system)
|
||||
systems = [system_mapping.get(system, system)]
|
||||
else:
|
||||
systems = get_synonyms(system, SYSTEM_SYNONYMS)
|
||||
|
||||
arch = platform.machine()
|
||||
if arch_mapping:
|
||||
arch = arch_mapping.get(arch, arch)
|
||||
archs = [arch_mapping.get(arch, arch)]
|
||||
else:
|
||||
archs = get_synonyms(arch, ARCH_SYNONYMS)
|
||||
|
||||
expected_names = {
|
||||
format.format(
|
||||
version=normalized_version,
|
||||
system=system,
|
||||
arch=arch,
|
||||
version=version_opt,
|
||||
system=system_opt,
|
||||
arch=arch_opt,
|
||||
)
|
||||
for normalized_version in (
|
||||
for version_opt, system_opt, arch_opt in product(
|
||||
(
|
||||
version.lstrip("v"),
|
||||
"v" + version if not version.startswith("v") else version,
|
||||
),
|
||||
systems,
|
||||
archs,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from itertools import chain
|
||||
from itertools import product
|
||||
from tarfile import TarFile
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import NamedTuple
|
||||
from typing import Optional
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import mock_open
|
||||
from unittest.mock import patch
|
||||
@ -21,10 +22,11 @@ class TestExpression(NamedTuple):
|
||||
args: list[Any]
|
||||
kwargs: dict[str, Any]
|
||||
expected: Any
|
||||
exception: Optional[type[Exception]] = None
|
||||
exception: type[Exception] | None = None
|
||||
msg: str | None = None
|
||||
|
||||
def run(self, f: Callable):
|
||||
with self.t.subTest(f=f, args=self.args, kwargs=self.kwargs):
|
||||
with self.t.subTest(msg=self.msg, f=f, args=self.args, kwargs=self.kwargs):
|
||||
try:
|
||||
result = f(*self.args, **self.kwargs)
|
||||
self.t.assertIsNone(
|
||||
@ -197,5 +199,277 @@ class TestContentTypeDetection(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestMatchAsset(unittest.TestCase):
|
||||
def test_match_asset_versions(self, *_):
|
||||
# Input variations:
|
||||
# Case 1: Version provided with prefix
|
||||
# Case 2: Version provided without prefix
|
||||
# Case 3: No version provided, tag exists in release
|
||||
# These should be impossible
|
||||
# Case 4: No version provided, tag doesn't exist in release but not in template
|
||||
# Case 5: No version provided, tag doesn't exist in release and is in template
|
||||
|
||||
# Release variations:
|
||||
# Case 1: tag_name with version prefix
|
||||
# Case 2: tag_name without version prefix
|
||||
|
||||
# File variations:
|
||||
# Case 1: file name with version prefix
|
||||
# Case 2: file name without version prefix
|
||||
|
||||
def new_expression(version: str | None, tag_name: str, file_name: str):
|
||||
release = {"tag_name": tag_name, "assets": [{"name": file_name}]}
|
||||
expected = {"name": file_name}
|
||||
return TestExpression(
|
||||
self, [release, "file-{version}.zip", version], {}, expected
|
||||
)
|
||||
|
||||
happy_cases = [
|
||||
new_expression(version, tag_name, file_name)
|
||||
for version, tag_name, file_name in product(
|
||||
("v1.0.0", "1.0.0", None),
|
||||
("v1.0.0", "1.0.0"),
|
||||
("file-v1.0.0.zip", "file-1.0.0.zip"),
|
||||
)
|
||||
]
|
||||
for test_case in happy_cases:
|
||||
test_case.run(release_gitter.match_asset)
|
||||
|
||||
def test_match_asset_systems(self, *_):
|
||||
# Input variations:
|
||||
# Case 1: System mapping provided
|
||||
# Case 2: No system mapping provided
|
||||
|
||||
# Test: We want to show that default matching will work out of the box with some values for the current machine
|
||||
# Test: We want to show that non-standard mappings will always work if provided manually
|
||||
|
||||
def run_with_context(actual_system: str, *args, **kwargs):
|
||||
with patch("platform.system", return_value=actual_system):
|
||||
return release_gitter.match_asset(*args, **kwargs)
|
||||
|
||||
def new_expression(
|
||||
actual_system: str,
|
||||
system_mapping: dict[str, str] | None,
|
||||
file_name: str,
|
||||
expected: dict[str, str],
|
||||
exception: type[Exception] | None = None,
|
||||
msg: str | None = None,
|
||||
):
|
||||
release = {
|
||||
"name": "v1.0.0",
|
||||
"tag_name": "v1.0.0",
|
||||
"assets": [{"name": file_name}],
|
||||
}
|
||||
return TestExpression(
|
||||
self,
|
||||
[actual_system, release, "file-{system}.zip"],
|
||||
{"system_mapping": system_mapping},
|
||||
expected,
|
||||
exception,
|
||||
msg,
|
||||
)
|
||||
|
||||
test_cases = chain(
|
||||
[
|
||||
new_expression(
|
||||
"Earth",
|
||||
None,
|
||||
"file-Earth.zip",
|
||||
{"name": "file-Earth.zip"},
|
||||
msg="Current system always included as an exact match synonym",
|
||||
),
|
||||
new_expression(
|
||||
"Linux",
|
||||
{"Linux": "jumanji"},
|
||||
"file-jumanji.zip",
|
||||
{"name": "file-jumanji.zip"},
|
||||
msg="Non-standard system mapping works",
|
||||
),
|
||||
new_expression(
|
||||
"Linux",
|
||||
{},
|
||||
"file-darwin.zip",
|
||||
{},
|
||||
ValueError,
|
||||
msg="No matching system",
|
||||
),
|
||||
],
|
||||
# Test default mappings
|
||||
(
|
||||
new_expression(
|
||||
actual_system,
|
||||
None,
|
||||
file_name,
|
||||
{"name": file_name},
|
||||
msg="Default Linux mappings",
|
||||
)
|
||||
for actual_system, file_name in product(
|
||||
("Linux", "linux"),
|
||||
("file-Linux.zip", "file-linux.zip"),
|
||||
)
|
||||
),
|
||||
(
|
||||
new_expression(
|
||||
actual_system,
|
||||
None,
|
||||
file_name,
|
||||
{"name": file_name},
|
||||
msg="Default macOS mappings",
|
||||
)
|
||||
for actual_system, file_name in product(
|
||||
("Darwin", "darwin", "MacOS", "macos", "macOS"),
|
||||
(
|
||||
"file-Darwin.zip",
|
||||
"file-darwin.zip",
|
||||
"file-MacOS.zip",
|
||||
"file-macos.zip",
|
||||
),
|
||||
)
|
||||
),
|
||||
(
|
||||
new_expression(
|
||||
actual_system,
|
||||
None,
|
||||
file_name,
|
||||
{"name": file_name},
|
||||
msg="Default Windows mappings",
|
||||
)
|
||||
for actual_system, file_name in product(
|
||||
("Windows", "windows", "win", "win32", "win64"),
|
||||
(
|
||||
"file-Windows.zip",
|
||||
"file-windows.zip",
|
||||
"file-win.zip",
|
||||
"file-win32.zip",
|
||||
"file-win64.zip",
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
for test_case in test_cases:
|
||||
test_case.run(run_with_context)
|
||||
|
||||
def test_match_asset_archs(self, *_):
|
||||
# Input variations:
|
||||
# Case 1: Arch mapping provided
|
||||
# Case 2: No arch mapping provided
|
||||
|
||||
# Test: We want to show that default matching will work out of the box with some values for the current machine
|
||||
# Test: We want to show that non-standard mappings will always work if provided manually
|
||||
|
||||
def run_with_context(actual_arch: str, *args, **kwargs):
|
||||
with patch("platform.machine", return_value=actual_arch):
|
||||
return release_gitter.match_asset(*args, **kwargs)
|
||||
|
||||
def new_expression(
|
||||
actual_arch: str,
|
||||
arch_mapping: dict[str, str] | None,
|
||||
file_name: str,
|
||||
expected: dict[str, str],
|
||||
exception: type[Exception] | None = None,
|
||||
msg: str | None = None,
|
||||
):
|
||||
release = {
|
||||
"name": "v1.0.0",
|
||||
"tag_name": "v1.0.0",
|
||||
"assets": [{"name": file_name}],
|
||||
}
|
||||
return TestExpression(
|
||||
self,
|
||||
[actual_arch, release, "file-{arch}.zip"],
|
||||
{"arch_mapping": arch_mapping},
|
||||
expected,
|
||||
exception,
|
||||
msg,
|
||||
)
|
||||
|
||||
test_cases = chain(
|
||||
[
|
||||
new_expression(
|
||||
"Earth",
|
||||
None,
|
||||
"file-Earth.zip",
|
||||
{"name": "file-Earth.zip"},
|
||||
msg="Current arch always included as an exact match synonym",
|
||||
),
|
||||
new_expression(
|
||||
"x86_64",
|
||||
{"x86_64": "jumanji"},
|
||||
"file-jumanji.zip",
|
||||
{"name": "file-jumanji.zip"},
|
||||
msg="Non-standard arch mapping works",
|
||||
),
|
||||
new_expression(
|
||||
"x86_64",
|
||||
{},
|
||||
"file-arm.zip",
|
||||
{},
|
||||
ValueError,
|
||||
msg="No matching arch",
|
||||
),
|
||||
],
|
||||
# Test default mappings
|
||||
(
|
||||
new_expression(
|
||||
actual_arch,
|
||||
None,
|
||||
file_name,
|
||||
{"name": file_name},
|
||||
msg="Default arm mappings",
|
||||
)
|
||||
for actual_arch, file_name in product(
|
||||
("arm",),
|
||||
("file-arm.zip",),
|
||||
)
|
||||
),
|
||||
(
|
||||
new_expression(
|
||||
actual_arch,
|
||||
None,
|
||||
file_name,
|
||||
{"name": file_name},
|
||||
msg="Default amd64 mappings",
|
||||
)
|
||||
for actual_arch, file_name in product(
|
||||
("amd64", "x86_64", "AMD64"),
|
||||
("file-amd64.zip", "file-x86_64.zip"),
|
||||
)
|
||||
),
|
||||
(
|
||||
new_expression(
|
||||
actual_arch,
|
||||
None,
|
||||
file_name,
|
||||
{"name": file_name},
|
||||
msg="Default arm64 mappings",
|
||||
)
|
||||
for actual_arch, file_name in product(
|
||||
("arm64", "aarch64", "armv8b", "armv8l"),
|
||||
(
|
||||
"file-arm64.zip",
|
||||
"file-aarch64.zip",
|
||||
"file-armv8b.zip",
|
||||
"file-armv8l.zip",
|
||||
),
|
||||
)
|
||||
),
|
||||
(
|
||||
new_expression(
|
||||
actual_arch,
|
||||
None,
|
||||
file_name,
|
||||
{"name": file_name},
|
||||
msg="Default x86 mappings",
|
||||
)
|
||||
for actual_arch, file_name in product(
|
||||
("x86", "i386", "i686"),
|
||||
("file-x86.zip", "file-i386.zip", "file-i686.zip"),
|
||||
)
|
||||
),
|
||||
)
|
||||
for test_case in test_cases:
|
||||
test_case.run(run_with_context)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
Loading…
Reference in New Issue
Block a user