diff --git a/.github/workflows/python-upload-test.yml b/.github/workflows/python-upload-test.yml index c1d67fb3..4c8af786 100644 --- a/.github/workflows/python-upload-test.yml +++ b/.github/workflows/python-upload-test.yml @@ -19,10 +19,10 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install --upgrade setuptools setuptools-rust build + python -m pip install --upgrade setuptools setuptools-rust build packaging - - name: Make .devXX version - run: python ./python/latest_dev_version.py + - name: Modify version for TestPyPI upload + run: python ./python/modify_version_for_testpypi.py - name: Build sdist working-directory: ./python @@ -52,8 +52,18 @@ jobs: target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Make .devXX version - run: python ./python/latest_dev_version.py + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U packaging + + - name: Modify version for TestPyPI upload + run: python ./python/modify_version_for_testpypi.py - uses: eiennohito/gha-manylinux-build@master with: @@ -89,14 +99,14 @@ jobs: target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Make .devXX version - run: python ./python/latest_dev_version.py - - name: Install dependencies run: | python -m pip install -U pip - python -m pip install -U setuptools setuptools_rust build + python -m pip install -U setuptools setuptools_rust build packaging + - name: Modify version for TestPyPI upload + run: python ./python/modify_version_for_testpypi.py + - name: Add aarch64/x86 target for Rust run: rustup target add aarch64-apple-darwin x86_64-apple-darwin if: startsWith(matrix.os, 'macOS') diff --git a/python/latest_dev_version.py b/python/latest_dev_version.py deleted file mode 100755 index d4d1f02f..00000000 --- a/python/latest_dev_version.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2021 Works Applications Co., Ltd. -# -# 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. - -# Set the version in setup.py to the next unused .dev version -# Used versions are acquired directly from - -import json -import re -import sys -import urllib.request -from pathlib import Path - -cur_file = Path(__file__) - -setup_py = cur_file.parent / "setup.py" - -with setup_py.open("rt", encoding="utf-8") as f: - setup_py_data = f.read() - -version_re = re.compile('version="([^"]+)",') -cur_version = version_re.findall(setup_py_data) - -if len(cur_version) != 1: - print("could not find version", sys.stderr) - exit(1) - -cur_version = cur_version[0] - -print("Current version:", cur_version) - -if "dev" in cur_version: - print("Can't modify dev version") - exit(1) - -response = urllib.request.urlopen("https://test.pypi.org/pypi/SudachiPy/json") -data = json.loads(response.read()) - -remote_versions = set(data["releases"].keys()) - -remote_versions.add("0.6.0") # it was deleted - -next_version_re = re.compile(r"""^(.*)\.dev(\d+)$""") - - -def next_version(version): - version = version.replace("-", "") # allow e.g. "v1.2.3-a4" - m = next_version_re.match(version) - if m is None: - return version + ".dev1" - else: - p1 = m.group(1) - p2 = int(m.group(2)) - return "{}.dev{}".format(p1, p2 + 1) - - -print("Remote versions:", sorted(remote_versions)) - -next_v = next_version(cur_version) - -while next_v in remote_versions: - next_v = next_version(next_v) - -print("::notice::Next version:", next_v) - -modified_setup_py = version_re.sub('version="{}",'.format(next_v), setup_py_data, 1) - -with setup_py.open("wt", encoding='utf-8') as f: - f.write(modified_setup_py) diff --git a/python/modify_version_for_testpypi.py b/python/modify_version_for_testpypi.py new file mode 100755 index 00000000..e93ccd40 --- /dev/null +++ b/python/modify_version_for_testpypi.py @@ -0,0 +1,101 @@ +# Copyright (c) 2021-2024 Works Applications Co., Ltd. +# +# 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. + +# Set the version in setup.py to the next unused version. +# This script is used to upload to TestPyPI (that does not allow same version) in python-upload-test workflow. +# +# 1. if current version has pre/post/dev part, increment the last part +# 2. if current version is final version, add post part +# +# we should avoid `.devN` if possible, since it's hard to handle version order with it. +# e.g. `1.2.dev1` < `1.2a1.dev1` < `1.2a1` < `1.2` +# ref: https://packaging.python.org/en/latest/specifications/version-specifiers/ + +import json +import re +import sys +import urllib.request +from pathlib import Path +from packaging.version import Version, InvalidVersion + +# find current version +cur_file = Path(__file__) +setup_py = cur_file.parent / "setup.py" + +with setup_py.open("rt", encoding="utf-8") as f: + setup_py_data = f.read() + +version_re = re.compile('version="([^"]+)",') +cur_version = version_re.findall(setup_py_data) + +if len(cur_version) != 1: + print("could not find version", sys.stderr) + exit(1) + +try: + cur_version = Version(cur_version[0]) + print("Current version:", cur_version) +except InvalidVersion: + print(f"{cur_version} is invalid as a python version") + exit(1) + +# find remote versions (in TestPyPI) +response = urllib.request.urlopen("https://test.pypi.org/pypi/SudachiPy/json") +data = json.loads(response.read()) + +remote_versions = set(data["releases"].keys()) + +# add deleted version to the list +remote_versions.add("0.6.0") + +print("Remote versions:", sorted(remote_versions)) + + +def increment_version(v: Version): + pre = v.pre + post = v.post + dev = v.dev + + if v.is_devrelease: + dev += 1 + elif v.is_postrelease: + post += 1 + elif v.is_prerelease: + pre = (pre[0], pre[1]+1) + else: # is final release + post = 1 + + next = v.base_version + \ + ("" if pre is None else f"{pre[0]}{pre[1]}") + \ + ("" if post is None else f".post{post}") + \ + ("" if dev is None else f".dev{dev}") + + assert Version(next) > v + return Version(next) + + +# search proper version to upload +next_v = cur_version + +while str(next_v) in remote_versions: + next_v = increment_version(next_v) + + +print("::notice::Next version:", next_v) + +modified_setup_py = version_re.sub( + 'version="{}",'.format(next_v), setup_py_data, 1) + +with setup_py.open("wt", encoding='utf-8') as f: + f.write(modified_setup_py) diff --git a/update_version.sh b/update_version.sh index e882a3ed..d3991598 100755 --- a/update_version.sh +++ b/update_version.sh @@ -4,6 +4,7 @@ set -eu if [ $# -lt 1 ] || ( [ $# -lt 2 ] && [ "$1" != "show" ] ) ; then echo "Provide 2 arguments [from] and [to] to update version, or 'show' to print current one." + echo "Note that the version should follow semantic-versioning and PEP440, e.g. '1.2.3' or '1.2.3-a4'" exit 1 fi