From 4c7b9981f43a72ef9bbe42eaa5de59c0b7d4fa28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Thu, 16 Nov 2023 19:14:35 +0200 Subject: [PATCH] feat: add some argument generator scripts --- README.md | 6 ++ generators/README.md | 50 +++++++++++ generators/wrun_black_args.py | 53 +++++++++++ generators/wrun_committed_args.py | 100 +++++++++++++++++++++ generators/wrun_dprint_args.py | 87 ++++++++++++++++++ generators/wrun_golangci_lint_args.py | 121 ++++++++++++++++++++++++++ generators/wrun_ruff_args.py | 100 +++++++++++++++++++++ generators/wrun_shellcheck_args.py | 59 +++++++++++++ generators/wrun_shfmt_args.py | 58 ++++++++++++ generators/wrun_terraform_args.py | 92 ++++++++++++++++++++ generators/wrun_tflint_args.py | 85 ++++++++++++++++++ generators/wrun_typos_args.py | 100 +++++++++++++++++++++ generators/wrun_vacuum_args.py | 91 +++++++++++++++++++ 13 files changed, 1002 insertions(+) create mode 100644 generators/README.md create mode 100755 generators/wrun_black_args.py create mode 100755 generators/wrun_committed_args.py create mode 100755 generators/wrun_dprint_args.py create mode 100755 generators/wrun_golangci_lint_args.py create mode 100755 generators/wrun_ruff_args.py create mode 100755 generators/wrun_shellcheck_args.py create mode 100755 generators/wrun_shfmt_args.py create mode 100755 generators/wrun_terraform_args.py create mode 100755 generators/wrun_tflint_args.py create mode 100755 generators/wrun_typos_args.py create mode 100755 generators/wrun_vacuum_args.py diff --git a/README.md b/README.md index ca0e078..45655dc 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,12 @@ If the `-use-pre-commit-cache` flag is given, a subdirectory in [pre-commit.ci](https://pre-commit.ci) is not supported, because it [disallows network access at runtime](https://github.com/pre-commit-ci/issues/issues/196#issuecomment-1810937079). +## Examples + +The [generators](generators/) directory contains scripts that can be +used to generate command line arguments for various tools commonly +used tools. + ## License SPDX-License-Identifier: Apache-2.0 diff --git a/generators/README.md b/generators/README.md new file mode 100644 index 0000000..c112448 --- /dev/null +++ b/generators/README.md @@ -0,0 +1,50 @@ +# wrun command line argument generators + +This directory contains scripts to generate wrun command line +arguments for various tools' releases. Generated arguments will +contain `-url`s, and `-archive-exe-path`s if applicable. + +The scripts are not robust against all kinds of changes that might be +occurring in upstream release assets, and may need tweaking at times. + +The intent is that they should work with the latest respective tool +release, and only that. Generators working with older versions might +be found in wrun Git history. + +## Usage + +The general usage is: + +```shell +python3 wrun_TOOL_args.py VERSION +``` + +...where `TOOL` is the tool in question, and `VERSION` is the version +to generate for, typically the Git tag rather than the numeric +version if they differ. + +The output is newline separated for readability. +Hint: if embedding to a YAML document as a string, e.g. a CI config, +using [line folding (`>-`)](https://yaml.org/spec/1.2.2/#65-line-folding) +the readability can likely be preserved there, too. + +## TODO + +Some tools for which generators would be nice to have, contributions welcome! + +- trivy, https://trivy.dev, https://github.com/aquasecurity/trivy/releases +- typos, https://github.com/crate-ci/typos, https://github.com/crate-ci/typos + +## Non-TODO + +Some tools for which generators would be nice to have, but cannot be done, +at least yet at time of writing. + +Unless mentioned otherwise, reasoning is that there is no `wrun` +runnable asset available for the tool. + +- commitlint, https://commitlint.js.org +- gitlint, https://jorisroovers.com/gitlint/ +- perlcritic, https://github.com/Perl-Critic/Perl-Critic +- perltidy, https://github.com/perltidy/perltidy +- prettier, https://prettier.io diff --git a/generators/wrun_black_args.py b/generators/wrun_black_args.py new file mode 100755 index 0000000..12b784b --- /dev/null +++ b/generators/wrun_black_args.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_black_args.py -- generate wrun command line args for black + +* https://black.readthedocs.io +* https://github.com/psf/black/releases +""" + +import hashlib +import sys +from urllib.parse import urljoin, quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "black_linux": "linux/amd64", + "black_macos": "darwin/amd64", + "black_windows.exe": "windows/amd64", +} + + +def main(version: str) -> None: + base_url = f"https://github.com/psf/black/releases/download/{urlquote(version)}/" + + for filename, os_arch in file_os_archs.items(): + url = urljoin(base_url, filename) + with urlopen(url) as f: + hexdigest = hashlib.file_digest(f, "sha256").hexdigest() + + print(f"-url {os_arch}={url}#sha256-{hexdigest}") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_committed_args.py b/generators/wrun_committed_args.py new file mode 100755 index 0000000..73b3d81 --- /dev/null +++ b/generators/wrun_committed_args.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_committed_args.py -- generate wrun command line args for committed + +* https://github.com/crate-ci/committed +* https://pypi.org/project/committed/#files +* https://warehouse.pypa.io/api-reference/json.html +""" + +import hashlib +import json +import sys +from urllib.parse import quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "committed-{version_number}-py3-none-macosx_10_7_x86_64.whl": "darwin/amd64", + "committed-{version_number}-py3-none-macosx_11_0_arm64.whl": "darwin/arm64", + "committed-{version_number}-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": None, # using musl one + "committed-{version_number}-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl": "linux/386", + "committed-{version_number}-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": None, # using musl one + "committed-{version_number}-py3-none-musllinux_1_2_aarch64.whl": "linux/arm64", + "committed-{version_number}-py3-none-musllinux_1_2_x86_64.whl": "linux/amd64", + "committed-{version_number}-py3-none-win32.whl": "windows/386", + "committed-{version_number}-py3-none-win_amd64.whl": "windows/amd64", +} + + +def check_hexdigest(url: str, algo: str, expected: str) -> None: + try: + assert len(expected) == len(hashlib.new(algo, b"canary").hexdigest()) + _ = bytes.fromhex(expected) + except Exception as e: + raise ValueError(f'not a {algo} hex digest: "{expected}"') from e + with urlopen(url) as f: + got = hashlib.file_digest(f, algo).hexdigest() + if got != expected: + raise ValueError(f'{algo} mismatch for "{url}", expected {expected}, got {got}') + + +def main(version: str) -> None: + project = "committed" + version_number = version.lstrip("v") + archive_exe_paths = [] + + release_url = f"https://pypi.org/pypi/{urlquote(project)}/{urlquote(version_number)}/json" + with urlopen(release_url) as f: + release_data = json.load(f) + + for url in release_data["urls"]: + if url["packagetype"] != "bdist_wheel": + continue + + filename = url["filename"] + + try: + hexdigest = url["digests"]["sha256"] + except KeyError as e: + raise KeyError(f"no sha256 digest available for {filename}") from e + + lookup_filename = filename.replace(f"-{version_number}-", "-{version_number}-", 1) + if lookup_filename not in file_os_archs: + raise KeyError(f'unhandled file: "{filename}"') + os_arch = file_os_archs[lookup_filename] + if os_arch is None: + continue + + check_hexdigest(url["url"], "sha256", hexdigest) + + print(f"-url {os_arch}={url["url"]}#sha256-{hexdigest}") + suffix = ".exe" if os_arch.startswith("windows/") else "" + archive_exe_paths.append( + f"-archive-exe-path {os_arch}={project}-{version_number}.data/scripts/{project}{suffix}" + ) + for p in archive_exe_paths: + print(p) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_dprint_args.py b/generators/wrun_dprint_args.py new file mode 100755 index 0000000..60edfab --- /dev/null +++ b/generators/wrun_dprint_args.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_dprint_args.py -- generate wrun command line args for dprint + +* https://dprint.dev +* https://github.com/dprint/dprint/releases +""" + +import hashlib +import sys +from urllib.parse import urljoin, quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "dprint-aarch64-apple-darwin.zip": "darwin/arm64", + "dprint-aarch64-unknown-linux-gnu.zip": None, # using musl one + "dprint-aarch64-unknown-linux-musl.zip": "linux/arm64", + "dprint-x86_64-apple-darwin.zip": "darwin/amd64", + "dprint-x86_64-pc-windows-msvc-installer.exe": None, + "dprint-x86_64-pc-windows-msvc.zip": "windows/amd64", + "dprint-x86_64-unknown-linux-gnu.zip": None, # using musl one + "dprint-x86_64-unknown-linux-musl.zip": "linux/amd64", +} + + +def check_hexdigest(url: str, algo: str, expected: str) -> None: + try: + assert len(expected) == len(hashlib.new(algo, b"canary").hexdigest()) + _ = bytes.fromhex(expected) + except Exception as e: + raise ValueError(f'not a {algo} hex digest: "{expected}"') from e + with urlopen(url) as f: + got = hashlib.file_digest(f, algo).hexdigest() + if got != expected: + raise ValueError(f'{algo} mismatch for "{url}", expected {expected}, got {got}') + + +def main(version: str) -> None: + base_url = ( + f"https://github.com/dprint/dprint/releases/download/{urlquote(version)}/" + ) + + with urlopen(urljoin(base_url, "SHASUMS256.txt")) as f: + for line in f: + sline = line.decode() + + hexdigest_filename = sline.split(maxsplit=3) + if len(hexdigest_filename) != 2: + raise ValueError(f'invalid checksums line: "{sline}"') + hexdigest, filename = hexdigest_filename + + if filename not in file_os_archs: + raise KeyError(f'unhandled file: "{filename}"') + os_arch = file_os_archs[filename] + if os_arch is None: + continue + + url = urljoin(base_url, filename) + check_hexdigest(url, "sha256", hexdigest) + + print(f"-url {os_arch}={url}#sha256-{hexdigest}") + print("-archive-exe-path windows/*=dprint.exe") + print("-archive-exe-path dprint") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_golangci_lint_args.py b/generators/wrun_golangci_lint_args.py new file mode 100755 index 0000000..448a614 --- /dev/null +++ b/generators/wrun_golangci_lint_args.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_golangci_lint_args.py -- generate wrun command line args for golangci-lint + +* https://golangci-lint.run +* https://github.com/golangci/golangci-lint/releases +""" + +import hashlib +import re +import sys +from urllib.parse import urljoin, quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "golangci-lint-{version_number}-darwin-amd64.tar.gz": "darwin/amd64", + "golangci-lint-{version_number}-darwin-arm64.tar.gz": "darwin/arm64", + "golangci-lint-{version_number}-freebsd-386.tar.gz": "freebsd/386", + "golangci-lint-{version_number}-freebsd-amd64.tar.gz": "freebsd/amd64", + "golangci-lint-{version_number}-freebsd-armv6.tar.gz": "freebsd/arm", + "golangci-lint-{version_number}-freebsd-armv7.tar.gz": None, # using armv6 one + "golangci-lint-{version_number}-illumos-amd64.tar.gz": "illumos/amd64", + "golangci-lint-{version_number}-linux-386.tar.gz": "linux/386", + "golangci-lint-{version_number}-linux-amd64.tar.gz": "linux/amd64", + "golangci-lint-{version_number}-linux-arm64.tar.gz": "linux/arm64", + "golangci-lint-{version_number}-linux-armv6.tar.gz": "linux/arm", + "golangci-lint-{version_number}-linux-armv7.tar.gz": None, # using armv6 one + "golangci-lint-{version_number}-linux-loong64.tar.gz": "linux/loong64", + "golangci-lint-{version_number}-linux-mips64.tar.gz": "linux/mips64", + "golangci-lint-{version_number}-linux-mips64le.tar.gz": "linux/mips64le", + "golangci-lint-{version_number}-linux-ppc64le.tar.gz": "linux/ppc64le", + "golangci-lint-{version_number}-linux-riscv64.tar.gz": "linux/riscv64", + "golangci-lint-{version_number}-linux-s390x.tar.gz": "linux/s390x", + "golangci-lint-{version_number}-netbsd-386.tar.gz": "netbsd/386", + "golangci-lint-{version_number}-netbsd-amd64.tar.gz": "netbsd/amd64", + "golangci-lint-{version_number}-netbsd-armv6.tar.gz": "netbsd/arm", + "golangci-lint-{version_number}-netbsd-armv7.tar.gz": None, # using armv6 one + "golangci-lint-{version_number}-source.tar.gz": None, + "golangci-lint-{version_number}-windows-386.zip": "windows/386", + "golangci-lint-{version_number}-windows-amd64.zip": "windows/amd64", + "golangci-lint-{version_number}-windows-arm64.zip": "windows/arm64", + "golangci-lint-{version_number}-windows-armv6.zip": "windows/arm", + "golangci-lint-{version_number}-windows-armv7.zip": None, # using armv6 one +} + + +def check_hexdigest(url: str, algo: str, expected: str) -> None: + try: + assert len(expected) == len(hashlib.new(algo, b"canary").hexdigest()) + _ = bytes.fromhex(expected) + except Exception as e: + raise ValueError(f'not a {algo} hex digest: "{expected}"') from e + with urlopen(url) as f: + got = hashlib.file_digest(f, algo).hexdigest() + if got != expected: + raise ValueError(f'{algo} mismatch for "{url}", expected {expected}, got {got}') + + +def main(version: str) -> None: + version_number = version.lstrip("v") + base_url = f"https://github.com/golangci/golangci-lint/releases/download/{urlquote(version)}/" + archive_exe_paths = [] + + with urlopen( + urljoin(base_url, urlquote(f"golangci-lint-{version_number}-checksums.txt")) + ) as f: + for line in f: + sline = line.decode() + + hexdigest_filename = sline.split(maxsplit=3) + if len(hexdigest_filename) != 2: + raise ValueError(f'invalid checksums line: "{sline}"') + hexdigest, filename = hexdigest_filename + + if filename.endswith(".deb") or filename.endswith(".rpm"): + continue + + lookup_filename = filename.replace( + f"-{version_number}-", "-{version_number}-", 1 + ) + if lookup_filename not in file_os_archs: + raise KeyError(f'unhandled file: "{filename}"') + os_arch = file_os_archs[lookup_filename] + if os_arch is None: + continue + + url = urljoin(base_url, filename) + check_hexdigest(url, "sha256", hexdigest) + + print(f"-url {os_arch}={url}#sha256-{hexdigest}") + dirname = re.sub(r"\.(t[\w.]+|zip)$", "", filename) + suffix = ".exe" if os_arch.startswith("windows/") else "" + archive_exe_paths.append( + f"-archive-exe-path {os_arch}={dirname}/golangci-lint{suffix}" + ) + for p in archive_exe_paths: + print(p) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_ruff_args.py b/generators/wrun_ruff_args.py new file mode 100755 index 0000000..6959a18 --- /dev/null +++ b/generators/wrun_ruff_args.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +""" +wrun_ruff_args.py -- generate wrun command line args for ruff + +* https://docs.astral.sh/ruff/ +* https://github.com/astral-sh/ruff/releases +""" + +import hashlib +import sys +from urllib.parse import urljoin, quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "ruff-aarch64-apple-darwin.tar.gz": "darwin/arm64", + "ruff-aarch64-pc-windows-msvc.zip": "windows/arm64", + "ruff-aarch64-unknown-linux-gnu.tar.gz": None, # using musl one + "ruff-aarch64-unknown-linux-musl.tar.gz": "linux/arm64", + "ruff-armv7-unknown-linux-gnueabihf.tar.gz": None, # using musl one + "ruff-armv7-unknown-linux-musleabihf.tar.gz": "linux/arm", + "ruff-i686-pc-windows-msvc.zip": "windows/386", + "ruff-i686-unknown-linux-gnu.tar.gz": None, # using musl one + "ruff-i686-unknown-linux-musl.tar.gz": "linux/386", + "ruff-powerpc64-unknown-linux-gnu.tar.gz": "linux/ppc64", + "ruff-powerpc64le-unknown-linux-gnu.tar.gz": "linux/ppc64le", + "ruff-s390x-unknown-linux-gnu.tar.gz": "linux/s390x", + "ruff-x86_64-apple-darwin.tar.gz": "darwin/amd64", + "ruff-x86_64-pc-windows-msvc.zip": "windows/amd64", + "ruff-x86_64-unknown-linux-gnu.tar.gz": None, # using musl one + "ruff-x86_64-unknown-linux-musl.tar.gz": "linux/amd64", +} + + +def check_hexdigest(url: str, algo: str, expected: str) -> None: + try: + assert len(expected) == len(hashlib.new(algo, b"canary").hexdigest()) + _ = bytes.fromhex(expected) + except Exception as e: + raise ValueError(f'not a {algo} hex digest: "{expected}"') from e + with urlopen(url) as f: + got = hashlib.file_digest(f, algo).hexdigest() + if got != expected: + raise ValueError(f'{algo} mismatch for "{url}", expected {expected}, got {got}') + + +def main(version: str) -> None: + base_url = ( + f"https://github.com/astral-sh/ruff/releases/download/{urlquote(version)}/" + ) + + for fn, os_arch in file_os_archs.items(): + fn += ".sha256" + url = urljoin(base_url, urlquote(fn)) + with urlopen(url) as f: + for line in f: + sline = line.decode() + + hexdigest_filename = sline.split(maxsplit=3) + if len(hexdigest_filename) != 2: + raise ValueError(f'invalid checksums line in {fn}: "{sline}"') + hexdigest, filename = hexdigest_filename + + filename = filename.lstrip("*") # Kind of dangerous... + if filename not in file_os_archs: + raise KeyError(f'unhandled file: "{filename}"') + os_arch = file_os_archs[filename] + if os_arch is None: + continue + + url = urljoin(base_url, filename) + check_hexdigest(url, "sha256", hexdigest) + + print(f"-url {os_arch}={url}#sha256-{hexdigest}") + print("-archive-exe-path windows/*=ruff.exe") + print("-archive-exe-path ruff") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_shellcheck_args.py b/generators/wrun_shellcheck_args.py new file mode 100755 index 0000000..7a048bb --- /dev/null +++ b/generators/wrun_shellcheck_args.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_shellcheck_args.py -- generate wrun command line args for shellcheck + +* https://www.shellcheck.net +* https://github.com/koalaman/shellcheck/releases +""" + +import hashlib +import sys +from urllib.parse import urljoin, quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "shellcheck-{version}.darwin.x86_64.tar.xz": "darwin/amd64", + "shellcheck-{version}.linux.aarch64.tar.xz": "linux/arm64", + "shellcheck-{version}.linux.armv6hf.tar.xz": "linux/arm", + "shellcheck-{version}.linux.x86_64.tar.xz": "linux/amd64", + "shellcheck-{version}.zip": "windows/amd64", +} + + +def main(version: str) -> None: + base_url = ( + f"https://github.com/koalaman/shellcheck/releases/download/{urlquote(version)}/" + ) + + for fn, os_arch in file_os_archs.items(): + url = urljoin(base_url, urlquote(fn.format(version=version))) + with urlopen(url) as f: + digest = hashlib.file_digest(f, "sha256") + + print(f"-url {os_arch}={url}#sha256-{digest.hexdigest()}") + print("-archive-exe-path windows/*=shellcheck.exe") + print(f"-archive-exe-path shellcheck-{version}/shellcheck") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_shfmt_args.py b/generators/wrun_shfmt_args.py new file mode 100755 index 0000000..4689c52 --- /dev/null +++ b/generators/wrun_shfmt_args.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_shfmt_args.py -- generate wrun command line args for shfmt + +* https://github.com/mvdan/sh#shfmt +* https://github.com/mvdan/sh/releases +""" + +import hashlib +import sys +from urllib.parse import urljoin, quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "shfmt_{version}_darwin_amd64": "darwin/amd64", + "shfmt_{version}_darwin_arm64": "darwin/arm64", + "shfmt_{version}_linux_386": "linux/386", + "shfmt_{version}_linux_amd64": "linux/amd64", + "shfmt_{version}_linux_arm": "linux/arm", + "shfmt_{version}_linux_arm64": "linux/arm64", + "shfmt_{version}_windows_386.exe": "windows/386", + "shfmt_{version}_windows_amd64.exe": "windows/amd64", +} + + +def main(version: str) -> None: + base_url = f"https://github.com/mvdan/sh/releases/download/{urlquote(version)}/" + + for fn, os_arch in file_os_archs.items(): + url = urljoin(base_url, urlquote(fn.format(version=version))) + with urlopen(url) as f: + digest = hashlib.file_digest(f, "sha256") + + print(f"-url {os_arch}={url}#sha256-{digest.hexdigest()}") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_terraform_args.py b/generators/wrun_terraform_args.py new file mode 100755 index 0000000..07b4489 --- /dev/null +++ b/generators/wrun_terraform_args.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_terraform_args.py -- generate wrun command line args for terraform + +* https://www.terraform.io +* https://developer.hashicorp.com/terraform/install +""" + +import hashlib +import sys +from urllib.parse import urljoin, quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "terraform_{version}_darwin_amd64.zip": "darwin/amd64", + "terraform_{version}_darwin_arm64.zip": "darwin/arm64", + "terraform_{version}_freebsd_386.zip": "freebsd/386", + "terraform_{version}_freebsd_amd64.zip": "freebsd/amd64", + "terraform_{version}_freebsd_arm.zip": "freebsd/arm", + "terraform_{version}_linux_386.zip": "linux/386", + "terraform_{version}_linux_amd64.zip": "linux/amd64", + "terraform_{version}_linux_arm.zip": "linux/arm", + "terraform_{version}_linux_arm64.zip": "linux/arm64", + "terraform_{version}_openbsd_386.zip": "openbsd/386", + "terraform_{version}_openbsd_amd64.zip": "openbsd/amd64", + "terraform_{version}_solaris_amd64.zip": "solaris/amd64", + "terraform_{version}_windows_386.zip": "windows/386", + "terraform_{version}_windows_amd64.zip": "windows/amd64", +} + + +def check_hexdigest(url: str, algo: str, expected: str) -> None: + try: + assert len(expected) == len(hashlib.new(algo, b"canary").hexdigest()) + _ = bytes.fromhex(expected) + except Exception as e: + raise ValueError(f'not a {algo} hex digest: "{expected}"') from e + with urlopen(url) as f: + got = hashlib.file_digest(f, algo).hexdigest() + if got != expected: + raise ValueError(f'{algo} mismatch for "{url}", expected {expected}, got {got}') + + +def main(version: str) -> None: + base_url = f"https://releases.hashicorp.com/terraform/{urlquote(version)}/" + + with urlopen(urljoin(base_url, urlquote(f"terraform_{version}_SHA256SUMS"))) as f: + for line in f: + sline = line.decode() + + hexdigest_filename = sline.split(maxsplit=3) + if len(hexdigest_filename) != 2: + raise ValueError(f'invalid checksums line: "{sline}"') + hexdigest, filename = hexdigest_filename + + lookup_filename = filename.replace(f"_{version}_", "_{version}_", 1) + if lookup_filename not in file_os_archs: + raise KeyError(f'unhandled file: "{filename}"') + os_arch = file_os_archs[lookup_filename] + if os_arch is None: + continue + + url = urljoin(base_url, filename) + check_hexdigest(url, "sha256", hexdigest) + + print(f"-url {os_arch}={url}#sha256-{hexdigest}") + print("-archive-exe-path windows/*=terraform.exe") + print("-archive-exe-path terraform") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_tflint_args.py b/generators/wrun_tflint_args.py new file mode 100755 index 0000000..511bb0e --- /dev/null +++ b/generators/wrun_tflint_args.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_tflint_args.py -- generate wrun command line args for tflint + +* https://github.com/terraform-linters/tflint +* https://github.com/terraform-linters/tflint/releases +""" + +import hashlib +import sys +from urllib.parse import urljoin, quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "tflint_darwin_amd64.zip": "darwin/amd64", + "tflint_darwin_arm64.zip": "darwin/arm64", + "tflint_linux_386.zip": "linux/386", + "tflint_linux_amd64.zip": "linux/amd64", + "tflint_linux_arm.zip": "linux/arm", + "tflint_linux_arm64.zip": "linux/arm64", + "tflint_windows_386.zip": "windows/386", + "tflint_windows_amd64.zip": "windows/amd64", +} + + +def check_hexdigest(url: str, algo: str, expected: str) -> None: + try: + assert len(expected) == len(hashlib.new(algo, b"canary").hexdigest()) + _ = bytes.fromhex(expected) + except Exception as e: + raise ValueError(f'not a {algo} hex digest: "{expected}"') from e + with urlopen(url) as f: + got = hashlib.file_digest(f, algo).hexdigest() + if got != expected: + raise ValueError(f'{algo} mismatch for "{url}", expected {expected}, got {got}') + + +def main(version: str) -> None: + base_url = f"https://github.com/terraform-linters/tflint/releases/download/{urlquote(version)}/" + + with urlopen(urljoin(base_url, "checksums.txt")) as f: + for line in f: + sline = line.decode() + + hexdigest_filename = sline.split(maxsplit=3) + if len(hexdigest_filename) != 2: + raise ValueError(f'invalid checksums line: "{sline}"') + hexdigest, filename = hexdigest_filename + + if filename not in file_os_archs: + raise KeyError(f'unhandled file: "{filename}"') + os_arch = file_os_archs[filename] + if os_arch is None: + continue + + url = urljoin(base_url, filename) + check_hexdigest(url, "sha256", hexdigest) + + print(f"-url {os_arch}={url}#sha256-{hexdigest}") + print("-archive-exe-path windows/*=tflint.exe") + print("-archive-exe-path tflint") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_typos_args.py b/generators/wrun_typos_args.py new file mode 100755 index 0000000..3a8f756 --- /dev/null +++ b/generators/wrun_typos_args.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_typos_args.py -- generate wrun command line args for typos + +* https://github.com/crate-ci/typos +* https://pypi.org/project/typos/#files +* https://warehouse.pypa.io/api-reference/json.html +""" + +import hashlib +import json +import sys +from urllib.parse import quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "typos-{version_number}-py3-none-macosx_10_7_x86_64.whl": "darwin/amd64", + "typos-{version_number}-py3-none-macosx_11_0_arm64.whl": "darwin/arm64", + "typos-{version_number}-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": None, # using musl one + "typos-{version_number}-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl": "linux/386", + "typos-{version_number}-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": None, # using musl one + "typos-{version_number}-py3-none-musllinux_1_2_aarch64.whl": "linux/arm64", + "typos-{version_number}-py3-none-musllinux_1_2_x86_64.whl": "linux/amd64", + "typos-{version_number}-py3-none-win32.whl": "windows/386", + "typos-{version_number}-py3-none-win_amd64.whl": "windows/amd64", +} + + +def check_hexdigest(url: str, algo: str, expected: str) -> None: + try: + assert len(expected) == len(hashlib.new(algo, b"canary").hexdigest()) + _ = bytes.fromhex(expected) + except Exception as e: + raise ValueError(f'not a {algo} hex digest: "{expected}"') from e + with urlopen(url) as f: + got = hashlib.file_digest(f, algo).hexdigest() + if got != expected: + raise ValueError(f'{algo} mismatch for "{url}", expected {expected}, got {got}') + + +def main(version: str) -> None: + project = "typos" + version_number = version.lstrip("v") + archive_exe_paths = [] + + release_url = f"https://pypi.org/pypi/{urlquote(project)}/{urlquote(version_number)}/json" + with urlopen(release_url) as f: + release_data = json.load(f) + + for url in release_data["urls"]: + if url["packagetype"] != "bdist_wheel": + continue + + filename = url["filename"] + + try: + hexdigest = url["digests"]["sha256"] + except KeyError as e: + raise KeyError(f"no sha256 digest available for {filename}") from e + + lookup_filename = filename.replace(f"-{version_number}-", "-{version_number}-", 1) + if lookup_filename not in file_os_archs: + raise KeyError(f'unhandled file: "{filename}"') + os_arch = file_os_archs[lookup_filename] + if os_arch is None: + continue + + check_hexdigest(url["url"], "sha256", hexdigest) + + print(f"-url {os_arch}={url["url"]}#sha256-{hexdigest}") + suffix = ".exe" if os_arch.startswith("windows/") else "" + archive_exe_paths.append( + f"-archive-exe-path {os_arch}={project}-{version_number}.data/scripts/{project}{suffix}" + ) + for p in archive_exe_paths: + print(p) + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1]) diff --git a/generators/wrun_vacuum_args.py b/generators/wrun_vacuum_args.py new file mode 100755 index 0000000..ddae41d --- /dev/null +++ b/generators/wrun_vacuum_args.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 + +# Copyright 2023 Ville Skyttä +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +wrun_vacuum_args.py -- generate wrun command line args for vacuum + +* https://quobix.com/vacuum/ +* https://github.com/daveshanley/vacuum/releases +""" + +import hashlib +import sys +from urllib.parse import urljoin, quote as urlquote +from urllib.request import urlopen + +file_os_archs = { + "vacuum_{version_number}_darwin_arm64.tar.gz": "darwin/arm64", + "vacuum_{version_number}_darwin_x86_64.tar.gz": "darwin/amd64", + "vacuum_{version_number}_linux_arm64.tar.gz": "linux/arm64", + "vacuum_{version_number}_linux_i386.tar.gz": "linux/386", + "vacuum_{version_number}_linux_x86_64.tar.gz": "linux/amd64", + "vacuum_{version_number}_windows_arm64.tar.gz": "windows/arm64", + "vacuum_{version_number}_windows_i386.tar.gz": "windows/386", + "vacuum_{version_number}_windows_x86_64.tar.gz": "windows/amd64", +} + + +def check_hexdigest(url: str, algo: str, expected: str) -> None: + try: + assert len(expected) == len(hashlib.new(algo, b"canary").hexdigest()) + _ = bytes.fromhex(expected) + except Exception as e: + raise ValueError(f'not a {algo} hex digest: "{expected}"') from e + with urlopen(url) as f: + got = hashlib.file_digest(f, algo).hexdigest() + if got != expected: + raise ValueError(f'{algo} mismatch for "{url}", expected {expected}, got {got}') + + +def main(version: str) -> None: + version_number = version.lstrip("v") + base_url = ( + f"https://github.com/daveshanley/vacuum/releases/download/{urlquote(version)}/" + ) + + with urlopen(urljoin(base_url, "checksums.txt")) as f: + for line in f: + sline = line.decode() + + hexdigest_filename = sline.split(maxsplit=3) + if len(hexdigest_filename) != 2: + raise ValueError(f'invalid checksums line: "{sline}"') + hexdigest, filename = hexdigest_filename + + lookup_filename = filename.replace( + f"_{version_number}_", "_{version_number}_", 1 + ) + if lookup_filename not in file_os_archs: + raise KeyError(f'unhandled file: "{filename}"') + os_arch = file_os_archs[lookup_filename] + if os_arch is None: + continue + + url = urljoin(base_url, filename) + check_hexdigest(url, "sha256", hexdigest) + + print(f"-url {os_arch}={url}#sha256-{hexdigest}") + print("-archive-exe-path windows/*=vacuum.exe") + print("-archive-exe-path vacuum") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} VERSION") + sys.exit(2) + main(sys.argv[1])