Skip to content

Commit afc86de

Browse files
authored
Implement automatic package updates (#4)
1 parent 8999266 commit afc86de

File tree

4 files changed

+131
-23
lines changed

4 files changed

+131
-23
lines changed

.github/workflows/update-package.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Automated package update
2+
3+
on:
4+
workflow_dispatch:
5+
6+
jobs:
7+
update-package:
8+
runs-on: ubuntu-latest
9+
env:
10+
GH_TOKEN: ${{ secrets.AUTO_UPDATE_PACKAGE }}
11+
steps:
12+
- name: Set branch name
13+
id: branch
14+
run: echo "BRANCH=autobot-update-$(date +'%Y%m%d-%H%M%S')" >> $GITHUB_OUTPUT
15+
- name: Checkout package
16+
uses: actions/checkout@v4
17+
- name: Set up Python
18+
uses: actions/setup-python@v4
19+
with:
20+
python-version: "3.10"
21+
- name: Get latest pydicom hash
22+
id: hash
23+
run: |
24+
CWD=$(pwd)
25+
git clone https://github.com/pydicom/pydicom ../pydicom
26+
cd ../pydicom
27+
echo "PYDICOM_HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
28+
cd $CWD
29+
- name: Install dependencies
30+
run: |
31+
pip install -U pip
32+
pip install mypy
33+
pip install -e ../pydicom
34+
- name: Update package
35+
run: |
36+
python scripts/update_package.py
37+
git add src/pydicom-stubs
38+
git status
39+
- name: Create pull request
40+
id: create_pr
41+
# Only creates a new PR if there are changes
42+
uses: peter-evans/create-pull-request@v6
43+
with:
44+
token: ${{ secrets.AUTO_UPDATE_PACKAGE }}
45+
title: "[update-bot] Update README.md"
46+
branch: ${{ steps.branch.outputs.BRANCH }}
47+
commit-message: "Automated package update"
48+
body: |
49+
Automated package update using https://github.com/pydicom/pydicom/commit/${{ steps.hash.outputs.PYDICOM_HASH }}
50+
- name: Automerge pull request
51+
if: ${{ steps.create_pr.outputs.pull-request-operation }} == 'created'
52+
env:
53+
BRANCH: ${{ steps.branch.outputs.BRANCH }}
54+
run: |
55+
gh pr merge --auto --delete-branch --squash "$BRANCH"

pyproject.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ version = "3.0.0.0.dev0"
2626
pydicom_stubs = ["*.pyi"]
2727

2828
[project.optional-dependencies]
29-
dev = ["mypy", "pydicom", "build"]
29+
dev = [
30+
"mypy",
31+
"pydicom @ git+https://github.com/pydicom/pydicom.git@main",
32+
"build",
33+
]
3034

3135
[project.urls]
3236
homepage = "https://github.com/pydicom/types-pydicom"
@@ -42,4 +46,4 @@ warn_unreachable = false
4246
ignore_missing_imports = true
4347
disallow_untyped_calls = true
4448
disallow_untyped_defs = true
45-
disallow_incomplete_defs = true
49+
disallow_incomplete_defs = true

scripts/generate_stubs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
BASE_DIRECTORY = Path(__file__).parent.parent
88
DS_DST = BASE_DIRECTORY / "custom" / "dataset.pyi"
9-
DS_SRC = BASE_DIRECTORY / "pydicom-stubs" / "dataset.pyi"
9+
DS_SRC = BASE_DIRECTORY / "src" / "pydicom-stubs" / "dataset.pyi"
1010

1111

1212
ElementDictType = dict[int, tuple[str, str, str, str]]

scripts/update_package.py

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import subprocess
66
import sys
77

8+
from pydicom import __version__
9+
810

911
BASE_DIRECTORY = Path(__file__).parent.parent
1012
SRC_DIRECTORY = BASE_DIRECTORY / "custom"
11-
DST_DIRECTORY = BASE_DIRECTORY / "pydicom-stubs"
13+
DST_DIRECTORY = BASE_DIRECTORY / "src" / "pydicom-stubs"
1214
PYDICOM_DIRECTORY = BASE_DIRECTORY.parent / "pydicom" / "src" / "pydicom"
1315

1416

@@ -31,45 +33,92 @@
3133
]
3234

3335

34-
if __name__ == "__main__":
36+
def update_stubs() -> None:
3537
# Clear out the stub files
3638
if DST_DIRECTORY.exists():
3739
shutil.rmtree(DST_DIRECTORY)
3840

39-
# Generate basic stub files using mypy's `stubgen`
41+
if (BASE_DIRECTORY / "pydicom").exists():
42+
shutil.rmtree(BASE_DIRECTORY / "pydicom")
43+
4044
print("Generating basic stub files with stubgen")
41-
subprocess.run(["which", "stubgen"], shell=True)
42-
returncode = subprocess.run(
43-
[
44-
f". {os.fspath(BASE_DIRECTORY / 'env' / 'env310' / 'bin' / 'activate')};"
45-
f"stubgen {os.fspath(PYDICOM_DIRECTORY)} -o .",
46-
],
45+
p = subprocess.run(
46+
[f"stubgen {os.fspath(PYDICOM_DIRECTORY)} -o ."],
4747
shell=True,
4848
)
4949

50-
if not list(DST_DIRECTORY.glob("*.pyi")):
50+
if p.returncode != 0:
5151
print(" Failed to generate the basic stub files")
5252
sys.exit(1)
5353

54-
# Generate the custom stub files
54+
print(f"Moving basic stub files to {DST_DIRECTORY}")
55+
shutil.move(BASE_DIRECTORY / "pydicom", DST_DIRECTORY)
56+
5557
print("Generating custom stub files")
5658
if not SRC_DIRECTORY.exists():
5759
SRC_DIRECTORY.mkdir(parents=True, exist_ok=True)
5860

59-
subprocess.run(
60-
[
61-
f". {os.fspath(BASE_DIRECTORY / 'env' / 'env310' / 'bin' / 'activate')};"
62-
"python scripts/generate_stubs.py",
63-
],
64-
shell=True,
65-
)
61+
subprocess.run(["python scripts/generate_stubs.py"], shell=True)
6662

67-
# Replace basic stub files with custom ones
6863
print("Replacing basic stub files with custom ones")
6964
for path in SRC_DIRECTORY.glob("*.pyi"):
7065
shutil.copyfile(path, DST_DIRECTORY / path.name)
7166

72-
# Remove unnecessary stubs
7367
print("Removing unnecessary stubs")
7468
for path in REMOVALS:
7569
path.unlink(missing_ok=True)
70+
71+
72+
def update_version() -> None:
73+
# Get the current package version
74+
typd_version = ""
75+
contents = []
76+
with open(BASE_DIRECTORY / "pyproject.toml", "r") as f:
77+
for line in f.readlines():
78+
contents.append(line.rstrip())
79+
if line.startswith("version = "):
80+
typd_version = line.rstrip()
81+
82+
# types-pydicom version: X.Y.Z.N[.dev0]
83+
typd_version = typd_version.strip("version = ")
84+
typd_version = typd_version.strip("\"")
85+
print(f"Found current package version '{typd_version}'")
86+
typd_version = typd_version.split(".")
87+
if not typd_version or len(typd_version) < 3:
88+
raise RuntimeError(
89+
f"Unable to determine the current package version from '{typd_version}'"
90+
)
91+
92+
# pydicom version: X.Y.Z[.dev0]
93+
pyd_version = __version__.strip().split(".")
94+
if len(pyd_version) not in (3, 4):
95+
raise RuntimeError(f"Unexpected pydicom version string '{pyd_version}'")
96+
97+
# Determine new package version
98+
if pyd_version[-1] == "dev0":
99+
# If pydicom is dev0 then use X.Y.Z.0.dev0:
100+
version = f"{'.'.join(pyd_version[:-1])}.0.dev0"
101+
elif pyd_version == typd_version[:3]:
102+
# If X.Y.Z match -> increment N
103+
version = f"{'.'.join(pyd_version)}.{int(typd_version[3]) + 1}"
104+
else:
105+
# If X.Y.Z don't match, use X.Y.Z.0
106+
version = f"{'.'.join(pyd_version)}.0"
107+
108+
if version.split(".") == typd_version:
109+
print(f"No package version change required")
110+
return
111+
112+
print(f"Changing package version to '{version}'")
113+
for idx, line in enumerate(contents):
114+
if line.startswith("version = "):
115+
contents[idx] = f"version = \"{version}\""
116+
break
117+
118+
with open(BASE_DIRECTORY / "pyproject.toml", "w") as f:
119+
f.write("\n".join(contents))
120+
121+
122+
if __name__ == "__main__":
123+
update_stubs()
124+
update_version()

0 commit comments

Comments
 (0)