From 500f4f40c54a66626adf4761f64182647830f2f7 Mon Sep 17 00:00:00 2001 From: Jerry Zhang Date: Wed, 17 Jun 2026 15:30:29 -0700 Subject: [PATCH] Add bazel build on python3.12 --- .bazelversion | 1 + .bcr/metadata.template.json | 13 +++ .bcr/presubmit.yml | 11 ++ .bcr/source.template.json | 5 + .coverage | Bin 0 -> 53248 bytes .github/workflows/bazel.yml | 25 +++++ .gitignore | 2 + BUILD.bazel | 197 +++++++++++++++++++++++++++++++++++ MODULE.bazel | 74 +++++++++++++ Makefile | 24 ++--- revup/__init__.py | 5 - revup/revup.py | 6 +- revup/version.py | 5 + scripts/build_manpage.sh | 40 +++++++ setup.cfg | 2 +- tests/test_version_parity.py | 35 +++++++ version.bzl | 8 ++ 17 files changed, 425 insertions(+), 28 deletions(-) create mode 100644 .bazelversion create mode 100644 .bcr/metadata.template.json create mode 100644 .bcr/presubmit.yml create mode 100644 .bcr/source.template.json create mode 100644 .coverage create mode 100644 .github/workflows/bazel.yml create mode 100644 BUILD.bazel create mode 100644 MODULE.bazel create mode 100644 revup/version.py create mode 100755 scripts/build_manpage.sh create mode 100644 tests/test_version_parity.py create mode 100644 version.bzl diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..e7fdef7 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +8.4.2 diff --git a/.bcr/metadata.template.json b/.bcr/metadata.template.json new file mode 100644 index 0000000..134e923 --- /dev/null +++ b/.bcr/metadata.template.json @@ -0,0 +1,13 @@ +{ + "homepage": "https://github.com/skydio/revup", + "maintainers": [ + { + "email": "jerry@skydio.com", + "github": "skydio", + "name": "Jerry Zhang" + } + ], + "repository": ["github:skydio/revup"], + "versions": [], + "yanked_versions": {} +} diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml new file mode 100644 index 0000000..bfd9a72 --- /dev/null +++ b/.bcr/presubmit.yml @@ -0,0 +1,11 @@ +matrix: + platform: ["debian11", "macos", "ubuntu2004"] + bazel: ["8.x"] + +tasks: + verify_targets: + name: Build the revup wheel + platform: ${{ platform }} + bazel: ${{ bazel }} + build_targets: + - "@revup//:revup" diff --git a/.bcr/source.template.json b/.bcr/source.template.json new file mode 100644 index 0000000..98a9be3 --- /dev/null +++ b/.bcr/source.template.json @@ -0,0 +1,5 @@ +{ + "url": "https://github.com/skydio/revup/releases/download/v{VERSION}/revup-v{VERSION}.tar.gz", + "integrity": "**leave this alone**", + "strip_prefix": "revup-{VERSION}" +} diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..98feca431748a29d64e528ff628f5ab1a09f469a GIT binary patch literal 53248 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.1.gz from docs/.md. Uses a genrule (rather +# than run_binary) so that the m4 make-variable from rules_m4's toolchain +# expands correctly. +[ + genrule( + name = "man1_{}".format(page), + srcs = ["docs/{}.md".format(page)], + outs = ["revup/man1/{}.1.gz".format(page)], + cmd = ( + "M4=$(M4) PANDOC=$(execpath :pandoc) " + + "$(execpath :build_manpage) " + + "{page} {version} '{date}' $(SRCS) $@" + ).format( + date = REVUP_DATE, + page = page, + version = REVUP_VERSION, + ), + tools = [ + ":build_manpage", + ":pandoc", + ], + toolchains = ["@rules_m4//m4:current_m4_toolchain"], + ) + for page in MAN_PAGES +] + +filegroup( + name = "man1", + srcs = ["revup/man1/{}.1.gz".format(page) for page in MAN_PAGES], +) + +# Rewrite docs/images/* links in README.md to absolute GitHub URLs, matching +# the substitution that setup.py performs at sdist build time. +genrule( + name = "long_description", + srcs = ["README.md"], + outs = ["README.pypi.md"], + cmd = """\ +python3 - "$<" "$@" <<'PY' +import re, sys +src, dst = sys.argv[1], sys.argv[2] +text = open(src).read() +text = re.sub( + r"((?!PYPI_REMOVE).)*", + "", + text, + flags=re.MULTILINE | re.DOTALL, +) +text = text.replace( + "docs/images/", + "https://raw.githubusercontent.com/skydio/revup/{hash}/docs/images/", +) +open(dst, "w").write(text) +PY +""".format(hash = REVUP_VERSION_HASH), +) + +py_library( + name = "revup_lib", + srcs = glob(["revup/**/*.py"]), + data = [":man1"], + imports = ["."], + visibility = ["//visibility:public"], +) + +py_package( + name = "revup_pkg", + packages = ["revup"], + deps = [":revup_lib"], +) + +py_wheel( + name = "revup", + abi = "none", + author = "Jerry Zhang", + author_email = "jerry@skydio.com", + classifiers = [ + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Version Control", + "Topic :: Software Development :: Version Control :: Git", + "Programming Language :: Python", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3 :: Only", + "Environment :: Console", + "Typing :: Typed", + ], + description_content_type = "text/markdown", + description_file = ":long_description", + distribution = "revup", + entry_points = { + "console_scripts": ["revup = revup.__main__:_main"], + }, + extra_distinfo_files = { + "//:LICENSE": "LICENSE", + }, + homepage = "https://github.com/skydio/revup", + license = "MIT", + platform = "any", + project_urls = { + "Source": "https://github.com/skydio/revup", + "Bug Tracker": "https://github.com/skydio/revup/issues", + }, + python_requires = ">=3.10", + python_tag = "py3", + requires = [ + "aiohttp", + "argcomplete", + "async_lru", + "attrs", + "requests", + "rich", + "multidict", + "yarl", + "async_timeout", + "charset_normalizer", + "aiosignal", + ], + summary = "Revolutionary github tools. Effortlessly create multiple branches and pull requests.", + version = REVUP_VERSION, + visibility = ["//visibility:public"], + deps = [":revup_pkg"], +) diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..f2f6869 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,74 @@ +module( + name = "revup", + # Keep this in sync with revup/version.py REVUP_VERSION. MODULE.bazel does + # not support `load`, so the value cannot be imported from version.bzl. + version = "0.4.0", + compatibility_level = 1, +) + +bazel_dep(name = "bazel_skylib", version = "1.7.1") +bazel_dep(name = "platforms", version = "0.0.10") +bazel_dep(name = "rules_license", version = "1.0.0") +bazel_dep(name = "rules_m4", version = "0.3.bcr.1") +bazel_dep(name = "rules_python", version = "0.36.0") +bazel_dep(name = "rules_shell", version = "0.3.0") + +# Pandoc isn't on BCR, so we fetch the official prebuilt binaries directly. +# One http_archive per (os, arch); BUILD.bazel selects the right one. +http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +PANDOC_VERSION = "3.10" + +_PANDOC_BUILD = """\ +exports_files(["bin/pandoc"], visibility = ["//visibility:public"]) +""" + +_PANDOC_WINDOWS_BUILD = """\ +exports_files(["pandoc.exe"], visibility = ["//visibility:public"]) +""" + +http_archive( + name = "pandoc_linux_amd64", + url = "https://github.com/jgm/pandoc/releases/download/{v}/pandoc-{v}-linux-amd64.tar.gz".format(v = PANDOC_VERSION), + sha256 = "e0f8af62d0f267d22baa5bcefe6d5dda3a097ccc60de794b759fe03159923244", + strip_prefix = "pandoc-{}".format(PANDOC_VERSION), + build_file_content = _PANDOC_BUILD, +) + +http_archive( + name = "pandoc_linux_arm64", + url = "https://github.com/jgm/pandoc/releases/download/{v}/pandoc-{v}-linux-arm64.tar.gz".format(v = PANDOC_VERSION), + sha256 = "55413dfb0c1aec861641fe858f1f73e84848f3db497b1c0c02e62887ea76f4a4", + strip_prefix = "pandoc-{}".format(PANDOC_VERSION), + build_file_content = _PANDOC_BUILD, +) + +http_archive( + name = "pandoc_macos_amd64", + url = "https://github.com/jgm/pandoc/releases/download/{v}/pandoc-{v}-x86_64-macOS.zip".format(v = PANDOC_VERSION), + sha256 = "6334f4d9af7c9e37e761dfad56fa5507685f6d29724ebf31c4be6d5c654a3161", + strip_prefix = "pandoc-{}-x86_64".format(PANDOC_VERSION), + build_file_content = _PANDOC_BUILD, +) + +http_archive( + name = "pandoc_macos_arm64", + url = "https://github.com/jgm/pandoc/releases/download/{v}/pandoc-{v}-arm64-macOS.zip".format(v = PANDOC_VERSION), + sha256 = "d9cad01d96ae774a0dc8c8c45bb1ad3e4c5ff2cc2e24f45958f5f9b7974aee34", + strip_prefix = "pandoc-{}-arm64".format(PANDOC_VERSION), + build_file_content = _PANDOC_BUILD, +) + +http_archive( + name = "pandoc_windows_amd64", + url = "https://github.com/jgm/pandoc/releases/download/{v}/pandoc-{v}-windows-x86_64.zip".format(v = PANDOC_VERSION), + sha256 = "bb808d00fd58762299d64582a9b4c3e4b106cd929e62c5f19bcdcb496f1e54ae", + strip_prefix = "pandoc-{}".format(PANDOC_VERSION), + build_file_content = _PANDOC_WINDOWS_BUILD, +) + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + is_default = True, + python_version = "3.12", +) diff --git a/Makefile b/Makefile index 3fc6a08..84ab558 100644 --- a/Makefile +++ b/Makefile @@ -35,18 +35,8 @@ clean: rm -rf $(BUILD_DIR) rm -rf .mypy_cache -REVUP_VERSION:=$(shell $(PYTHON) revup/__init__.py) +REVUP_VERSION:=$(shell $(PYTHON) revup/version.py) REVUP_DATE ?= Apr 21, 2021 -define REVUP_HEADER ---- -title: TITLE -section: 1 -header: Revup Manual -footer: revup VERSION -date: DATE ---- -endef -export REVUP_HEADER REVUP_VERSION_HASH?=${shell git rev-parse --short v$(REVUP_VERSION) || echo main} @@ -66,13 +56,11 @@ upload: $(PYTHON) -m twine upload build/revup-$(REVUP_VERSION).tar.gz man: - mkdir -p revup/man1 ; \ - cd docs ; \ - for file in *.md ; do \ - CMD_NAME=`echo $${file} | awk -F'[.]' '{print $$1}'` ; \ - echo "$${REVUP_HEADER}" | m4 -DTITLE=$${CMD_NAME} -DVERSION=$(REVUP_VERSION) -DDATE="$(REVUP_DATE)" - | \ - cat - $${file} | pandoc -f markdown-smart -s -t man > ../revup/man1/$${CMD_NAME}.1 || exit 1 ; \ - gzip -n -f -k ../revup/man1/$${CMD_NAME}.1 || exit 1 ; \ + mkdir -p revup/man1 + @for src in docs/*.md ; do \ + name=$$(basename $${src} .md) ; \ + scripts/build_manpage.sh "$${name}" "$(REVUP_VERSION)" "$(REVUP_DATE)" \ + "$${src}" "revup/man1/$${name}.1.gz" || exit 1 ; \ done test: diff --git a/revup/__init__.py b/revup/__init__.py index 007c867..e69de29 100644 --- a/revup/__init__.py +++ b/revup/__init__.py @@ -1,5 +0,0 @@ -__version__ = "0.4.0" - -if __name__ == "__main__": - # So that Makefile can get the version without having to parse python - print(__version__) diff --git a/revup/revup.py b/revup/revup.py index 50d127e..2b1546d 100755 --- a/revup/revup.py +++ b/revup/revup.py @@ -8,7 +8,6 @@ import sys from typing import Any, List, Tuple -import revup from revup import config, git, logs, shell from revup.completion import ( ShellType, @@ -21,6 +20,7 @@ from revup.forge_utils import parse_forge_info from revup.topic_stack import PrBodySource from revup.types import RevupUsageException +from revup.version import REVUP_VERSION REVUP_CONFIG_ENV_VAR = "REVUP_CONFIG_PATH" CONFIG_FILE_NAME = ".revupconfig" @@ -47,9 +47,7 @@ def __call__(self, parser: Any, namespace: Any, values: Any, option_string: Any def make_toplevel_parser() -> RevupArgParser: revup_parser = RevupArgParser(add_help=False, prog="revup") revup_parser.add_argument("--help", "-h", action=HelpAction, nargs=0) - revup_parser.add_argument( - "--version", action="version", version=f"%(prog)s {revup.__version__}" - ) + revup_parser.add_argument("--version", action="version", version=f"%(prog)s {REVUP_VERSION}") revup_parser.add_argument("--proxy") revup_parser.add_argument("--forge-oauth", "--github-oauth") revup_parser.add_argument("--forge-url", "--github-url", default="github.com") diff --git a/revup/version.py b/revup/version.py new file mode 100644 index 0000000..c9de931 --- /dev/null +++ b/revup/version.py @@ -0,0 +1,5 @@ +REVUP_VERSION = "0.4.0" + +if __name__ == "__main__": + # Lets Makefile read the version without parsing a Python file. + print(REVUP_VERSION) diff --git a/scripts/build_manpage.sh b/scripts/build_manpage.sh new file mode 100755 index 0000000..ab47eb5 --- /dev/null +++ b/scripts/build_manpage.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Build a gzipped man page from a markdown source. +# +# Usage: build_manpage.sh +# +# Composes a pandoc-style YAML header with m4, prepends it to the markdown, +# pipes through pandoc, then gzip -n for reproducibility. Honors $M4 and +# $PANDOC env vars so Bazel can supply hermetic toolchains; falls back to PATH. +set -euo pipefail + +NAME=$1 +VERSION=$2 +DATE=$3 +INPUT=$4 +OUTPUT=$5 + +: "${M4:=m4}" +: "${PANDOC:=pandoc}" + +HEADER=$("$M4" \ + -DTITLE="$NAME" \ + -DVERSION="$VERSION" \ + -DDATE="$DATE" \ + <<'EOF' +--- +title: TITLE +section: 1 +header: Revup Manual +footer: revup VERSION +date: DATE +--- +EOF +) + +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT + +printf '%s\n' "$HEADER" | cat - "$INPUT" \ + | "$PANDOC" -f markdown-smart -s -t man > "$TMP/page.1" +gzip -n -c "$TMP/page.1" > "$OUTPUT" diff --git a/setup.cfg b/setup.cfg index f05bc12..dbcf4ea 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = revup -version = attr: revup.__version__ +version = attr: revup.version.REVUP_VERSION author = Jerry Zhang author_email = jerry@skydio.com description = Revolutionary github tools. Effortlessly create multiple branches and pull requests. diff --git a/tests/test_version_parity.py b/tests/test_version_parity.py new file mode 100644 index 0000000..fcb4d1b --- /dev/null +++ b/tests/test_version_parity.py @@ -0,0 +1,35 @@ +"""The version is duplicated in revup/version.py and version.bzl because +MODULE.bazel cannot `load()` and Bazel needs a Starlark-readable form. This +test catches drift between the two.""" + +import re +from pathlib import Path + +from revup.version import REVUP_VERSION + + +def _read_starlark_version(path: Path) -> str: + text = path.read_text() + match = re.search(r'^REVUP_VERSION\s*=\s*"([^"]+)"', text, re.MULTILINE) + assert match, f"REVUP_VERSION not found in {path}" + return match.group(1) + + +def _read_module_bazel_version(path: Path) -> str: + text = path.read_text() + match = re.search(r'^\s*version\s*=\s*"([^"]+)"', text, re.MULTILINE) + assert match, f"module() version not found in {path}" + return match.group(1) + + +def test_version_files_match(): + repo_root = Path(__file__).parent.parent + bzl_version = _read_starlark_version(repo_root / "version.bzl") + module_version = _read_module_bazel_version(repo_root / "MODULE.bazel") + + assert REVUP_VERSION == bzl_version, ( + f"revup/version.py ({REVUP_VERSION}) and version.bzl ({bzl_version}) disagree." + ) + assert REVUP_VERSION == module_version, ( + f"revup/version.py ({REVUP_VERSION}) and MODULE.bazel ({module_version}) disagree." + ) diff --git a/version.bzl b/version.bzl new file mode 100644 index 0000000..ea7cca5 --- /dev/null +++ b/version.bzl @@ -0,0 +1,8 @@ +"""Single source of truth for the revup version number. + +This file is intentionally a real file (not a symlink) for Windows +compatibility. A test (tests/test_version_parity.py) ensures it stays in sync +with revup/version.py, which Python imports. +""" + +REVUP_VERSION = "0.4.0"