diff --git a/.github/workflows/update-package.yml b/.github/workflows/update-package.yml new file mode 100644 index 0000000..8cd83f5 --- /dev/null +++ b/.github/workflows/update-package.yml @@ -0,0 +1,55 @@ +name: Automated package update + +on: + workflow_dispatch: + +jobs: + update-package: + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.AUTO_UPDATE_PACKAGE }} + steps: + - name: Set branch name + id: branch + run: echo "BRANCH=autobot-update-$(date +'%Y%m%d-%H%M%S')" >> $GITHUB_OUTPUT + - name: Checkout package + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Get latest pydicom hash + id: hash + run: | + CWD=$(pwd) + git clone https://github.com/pydicom/pydicom ../pydicom + cd ../pydicom + echo "PYDICOM_HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + cd $CWD + - name: Install dependencies + run: | + pip install -U pip + pip install mypy + pip install -e ../pydicom + - name: Update package + run: | + python scripts/update_package.py + git add src/pydicom-stubs + git status + - name: Create pull request + id: create_pr + # Only creates a new PR if there are changes + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.AUTO_UPDATE_PACKAGE }} + title: "[update-bot] Update README.md" + branch: ${{ steps.branch.outputs.BRANCH }} + commit-message: "Automated package update" + body: | + Automated package update using https://github.com/pydicom/pydicom/commit/${{ steps.hash.outputs.PYDICOM_HASH }} + - name: Automerge pull request + if: ${{ steps.create_pr.outputs.pull-request-operation }} == 'created' + env: + BRANCH: ${{ steps.branch.outputs.BRANCH }} + run: | + gh pr merge --auto --delete-branch --squash "$BRANCH" diff --git a/pyproject.toml b/pyproject.toml index 3d3cc32..c997dbf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,11 @@ version = "3.0.0.0.dev0" pydicom_stubs = ["*.pyi"] [project.optional-dependencies] -dev = ["mypy", "pydicom", "build"] +dev = [ + "mypy", + "pydicom @ git+https://github.com/pydicom/pydicom.git@main", + "build", +] [project.urls] homepage = "https://github.com/pydicom/types-pydicom" @@ -42,4 +46,4 @@ warn_unreachable = false ignore_missing_imports = true disallow_untyped_calls = true disallow_untyped_defs = true -disallow_incomplete_defs = true +disallow_incomplete_defs = true \ No newline at end of file diff --git a/scripts/generate_stubs.py b/scripts/generate_stubs.py index cd97da4..15f4ce3 100755 --- a/scripts/generate_stubs.py +++ b/scripts/generate_stubs.py @@ -6,7 +6,7 @@ BASE_DIRECTORY = Path(__file__).parent.parent DS_DST = BASE_DIRECTORY / "custom" / "dataset.pyi" -DS_SRC = BASE_DIRECTORY / "pydicom-stubs" / "dataset.pyi" +DS_SRC = BASE_DIRECTORY / "src" / "pydicom-stubs" / "dataset.pyi" ElementDictType = dict[int, tuple[str, str, str, str]] diff --git a/scripts/update_package.py b/scripts/update_package.py index 113df85..fea2509 100755 --- a/scripts/update_package.py +++ b/scripts/update_package.py @@ -5,10 +5,12 @@ import subprocess import sys +from pydicom import __version__ + BASE_DIRECTORY = Path(__file__).parent.parent SRC_DIRECTORY = BASE_DIRECTORY / "custom" -DST_DIRECTORY = BASE_DIRECTORY / "pydicom-stubs" +DST_DIRECTORY = BASE_DIRECTORY / "src" / "pydicom-stubs" PYDICOM_DIRECTORY = BASE_DIRECTORY.parent / "pydicom" / "src" / "pydicom" @@ -31,45 +33,92 @@ ] -if __name__ == "__main__": +def update_stubs() -> None: # Clear out the stub files if DST_DIRECTORY.exists(): shutil.rmtree(DST_DIRECTORY) - # Generate basic stub files using mypy's `stubgen` + if (BASE_DIRECTORY / "pydicom").exists(): + shutil.rmtree(BASE_DIRECTORY / "pydicom") + print("Generating basic stub files with stubgen") - subprocess.run(["which", "stubgen"], shell=True) - returncode = subprocess.run( - [ - f". {os.fspath(BASE_DIRECTORY / 'env' / 'env310' / 'bin' / 'activate')};" - f"stubgen {os.fspath(PYDICOM_DIRECTORY)} -o .", - ], + p = subprocess.run( + [f"stubgen {os.fspath(PYDICOM_DIRECTORY)} -o ."], shell=True, ) - if not list(DST_DIRECTORY.glob("*.pyi")): + if p.returncode != 0: print(" Failed to generate the basic stub files") sys.exit(1) - # Generate the custom stub files + print(f"Moving basic stub files to {DST_DIRECTORY}") + shutil.move(BASE_DIRECTORY / "pydicom", DST_DIRECTORY) + print("Generating custom stub files") if not SRC_DIRECTORY.exists(): SRC_DIRECTORY.mkdir(parents=True, exist_ok=True) - subprocess.run( - [ - f". {os.fspath(BASE_DIRECTORY / 'env' / 'env310' / 'bin' / 'activate')};" - "python scripts/generate_stubs.py", - ], - shell=True, - ) + subprocess.run(["python scripts/generate_stubs.py"], shell=True) - # Replace basic stub files with custom ones print("Replacing basic stub files with custom ones") for path in SRC_DIRECTORY.glob("*.pyi"): shutil.copyfile(path, DST_DIRECTORY / path.name) - # Remove unnecessary stubs print("Removing unnecessary stubs") for path in REMOVALS: path.unlink(missing_ok=True) + + +def update_version() -> None: + # Get the current package version + typd_version = "" + contents = [] + with open(BASE_DIRECTORY / "pyproject.toml", "r") as f: + for line in f.readlines(): + contents.append(line.rstrip()) + if line.startswith("version = "): + typd_version = line.rstrip() + + # types-pydicom version: X.Y.Z.N[.dev0] + typd_version = typd_version.strip("version = ") + typd_version = typd_version.strip("\"") + print(f"Found current package version '{typd_version}'") + typd_version = typd_version.split(".") + if not typd_version or len(typd_version) < 3: + raise RuntimeError( + f"Unable to determine the current package version from '{typd_version}'" + ) + + # pydicom version: X.Y.Z[.dev0] + pyd_version = __version__.strip().split(".") + if len(pyd_version) not in (3, 4): + raise RuntimeError(f"Unexpected pydicom version string '{pyd_version}'") + + # Determine new package version + if pyd_version[-1] == "dev0": + # If pydicom is dev0 then use X.Y.Z.0.dev0: + version = f"{'.'.join(pyd_version[:-1])}.0.dev0" + elif pyd_version == typd_version[:3]: + # If X.Y.Z match -> increment N + version = f"{'.'.join(pyd_version)}.{int(typd_version[3]) + 1}" + else: + # If X.Y.Z don't match, use X.Y.Z.0 + version = f"{'.'.join(pyd_version)}.0" + + if version.split(".") == typd_version: + print(f"No package version change required") + return + + print(f"Changing package version to '{version}'") + for idx, line in enumerate(contents): + if line.startswith("version = "): + contents[idx] = f"version = \"{version}\"" + break + + with open(BASE_DIRECTORY / "pyproject.toml", "w") as f: + f.write("\n".join(contents)) + + +if __name__ == "__main__": + update_stubs() + update_version()