-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #289 from scop/feat/pre-commit-py
feat(pre-commit): add binary based install
- Loading branch information
Showing
4 changed files
with
180 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
#!/usr/bin/env python3 | ||
import hashlib | ||
import http | ||
import io | ||
import os.path | ||
import stat | ||
import sys | ||
import tarfile | ||
import urllib.request | ||
import zipfile | ||
from distutils.command.build import build as orig_build | ||
from distutils.core import Command | ||
from typing import Tuple | ||
|
||
from setuptools import setup | ||
from setuptools.command.install import install as orig_install | ||
|
||
TYPOS_VERSION = '1.0.9' | ||
POSTFIX_SHA256 = { | ||
'linux': ( | ||
'x86_64-unknown-linux-gnu.tar.gz', | ||
'', # TODO: sha256 hexhash when we can generate it with release | ||
), | ||
'darwin': ( | ||
'x86_64-apple-darwin.tar.gz', | ||
'', # TODO: sha256 hexhash when we can generate it with release | ||
), | ||
'win32': ( | ||
'x86_64-pc-windows-msvc.zip', | ||
'', # TODO: sha256 hexhash when we can generate it with release | ||
), | ||
} | ||
PY_VERSION = '1' | ||
|
||
|
||
def get_download_url() -> Tuple[str, str]: | ||
postfix, sha256 = POSTFIX_SHA256[sys.platform] | ||
url = ( | ||
f'https://github.com/crate-ci/typos/releases/download/' | ||
f'v{TYPOS_VERSION}/typos-v{TYPOS_VERSION}-{postfix}' | ||
) | ||
return url, sha256 | ||
|
||
|
||
def download(url: str, sha256: str) -> bytes: | ||
with urllib.request.urlopen(url) as resp: | ||
code = resp.getcode() | ||
if code != http.HTTPStatus.OK: | ||
raise ValueError(f'HTTP failure. Code: {code}') | ||
data = resp.read() | ||
|
||
if not sha256: | ||
return data | ||
|
||
checksum = hashlib.sha256(data).hexdigest() | ||
if checksum != sha256: | ||
raise ValueError(f'sha256 mismatch, expected {sha256}, got {checksum}') | ||
|
||
return data | ||
|
||
|
||
def extract(url: str, data: bytes) -> bytes: | ||
with io.BytesIO(data) as bio: | ||
if '.tar.' in url: | ||
with tarfile.open(fileobj=bio) as tarf: | ||
for info in tarf.getmembers(): | ||
if info.isfile() and info.name.endswith('typos'): | ||
return tarf.extractfile(info).read() | ||
elif url.endswith('.zip'): | ||
with zipfile.ZipFile(bio) as zipf: | ||
for info in zipf.infolist(): | ||
if info.filename.endswith('.exe'): | ||
return zipf.read(info.filename) | ||
|
||
raise AssertionError(f'unreachable {url}') | ||
|
||
|
||
def save_executable(data: bytes, base_dir: str): | ||
exe = 'typos' if sys.platform != 'win32' else 'typos.exe' | ||
output_path = os.path.join(base_dir, exe) | ||
os.makedirs(base_dir) | ||
|
||
with open(output_path, 'wb') as fp: | ||
fp.write(data) | ||
|
||
# Mark as executable. | ||
# https://stackoverflow.com/a/14105527 | ||
mode = os.stat(output_path).st_mode | ||
mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | ||
os.chmod(output_path, mode) | ||
|
||
|
||
class build(orig_build): | ||
sub_commands = orig_build.sub_commands + [('fetch_binaries', None)] | ||
|
||
|
||
class install(orig_install): | ||
sub_commands = orig_install.sub_commands + [('install_typos', None)] | ||
|
||
|
||
class fetch_binaries(Command): | ||
build_temp = None | ||
|
||
def initialize_options(self): | ||
pass | ||
|
||
def finalize_options(self): | ||
self.set_undefined_options('build', ('build_temp', 'build_temp')) | ||
|
||
def run(self): | ||
# save binary to self.build_temp | ||
url, sha256 = get_download_url() | ||
archive = download(url, sha256) | ||
data = extract(url, archive) | ||
save_executable(data, self.build_temp) | ||
|
||
|
||
class install_typos(Command): | ||
description = 'install the typos executable' | ||
outfiles = () | ||
build_dir = install_dir = None | ||
|
||
def initialize_options(self): | ||
pass | ||
|
||
def finalize_options(self): | ||
# this initializes attributes based on other commands' attributes | ||
self.set_undefined_options('build', ('build_temp', 'build_dir')) | ||
self.set_undefined_options( | ||
'install', ('install_scripts', 'install_dir'), | ||
) | ||
|
||
def run(self): | ||
self.outfiles = self.copy_tree(self.build_dir, self.install_dir) | ||
|
||
def get_outputs(self): | ||
return self.outfiles | ||
|
||
|
||
command_overrides = { | ||
'install': install, | ||
'install_typos': install_typos, | ||
'build': build, | ||
'fetch_binaries': fetch_binaries, | ||
} | ||
|
||
|
||
try: | ||
from wheel.bdist_wheel import bdist_wheel as orig_bdist_wheel | ||
except ImportError: | ||
pass | ||
else: | ||
class bdist_wheel(orig_bdist_wheel): | ||
def finalize_options(self): | ||
orig_bdist_wheel.finalize_options(self) | ||
# Mark us as not a pure python package | ||
self.root_is_pure = False | ||
|
||
def get_tag(self): | ||
_, _, plat = orig_bdist_wheel.get_tag(self) | ||
# We don't contain any python source, nor any python extensions | ||
return 'py2.py3', 'none', plat | ||
|
||
command_overrides['bdist_wheel'] = bdist_wheel | ||
|
||
setup(version=f'{TYPOS_VERSION}.{PY_VERSION}', cmdclass=command_overrides) |