Skip to content

Commit 8b52559

Browse files
rahulr-NIdummy
andauthored
Support releasing individual packages in nimi-python (#2090)
* Update of changelog and python scripts to support the indivitual versioning . * Updated files are per new implementation * CHangelog Sample * New ChangeLog * Updated changelog * ni Dc power to Test * Added for nidmm and digital * Fixed for all drviers * Including seperator * Spacing issue fixed * Updated Contributing.md file * Updated alignment * Alignment fixed * Updated help in UpdateReleaseInfo.py * Accept only one input * Updated changelog and contributing.md * Contibuting MD version change. * changes for flake command * Will be moving the changelog changes to different PR * Included changes for code reveiw comments. * Revert "Included changes for code reveiw comments." This reverts commit 9de9d57. * Reapply "Included changes for code reveiw comments." This reverts commit cc126e8. * Minor bug fix. * Code Review comments implementations * Removed build version increment * Updated review comments * Code review comments incorporated. * Updating example.rst.mako and check_latest_release.yml file * Incorporated review comments. * check_latest_release.ywm file updated --------- Co-authored-by: dummy <[email protected]>
1 parent 4d2600d commit 8b52559

File tree

5 files changed

+140
-49
lines changed

5 files changed

+140
-49
lines changed

.github/workflows/check_latest_release.yml

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,23 @@ jobs:
2525
- x64
2626
- rdss-nimibot-win-10-py32
2727
timeout-minutes: 30
28-
strategy:
29-
matrix:
30-
module_name:
31-
- nidigital
32-
- nitclk
33-
- nifgen
34-
- nidcpower
35-
- nidmm
36-
- niscope
37-
- nimodinst
38-
- nise
39-
- niswitch
4028
steps:
4129
- name: checkout repository
4230
uses: actions/checkout@v3
31+
32+
- name: Extract module name and version from release tag
33+
id: extract_tag
34+
run: |
35+
# Extract module name and version from the release tag
36+
# Assuming the tag format is <module_name>-<version>, e.g., nidigital-1.4.0
37+
TAG="${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.event.release.tag_name }}"
38+
MODULE_NAME=$(echo "$TAG" | cut -d'-' -f1)
39+
MODULE_VERSION=$(echo "$TAG" | cut -d'-' -f2-)
40+
echo "module_name=$MODULE_NAME" >> "$GITHUB_OUTPUT"
41+
echo "module_version=$MODULE_VERSION" >> "$GITHUB_OUTPUT"
4342
# NOTE: we don't upload test coverage for this
4443
- name: run examples using PyPI uploads
4544
uses: ./.github/actions/run_examples_using_pypi_uploads
4645
with:
47-
module_name: ${{ matrix.module_name }}
48-
module_version: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.event.release.tag_name }}
46+
module_name: ${{ steps.extract_tag.outputs.module_name }}
47+
module_version: ${{ steps.extract_tag.outputs.module_version }}

CONTRIBUTING.md

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,17 @@ Release Process
149149
```
150150
1. Ensure no commits are made on ni/nimi-python/master until the release is complete
151151
1. Create and checkout a branch for release-related changes
152+
1. Perform Version Bump (If Needed)
153+
* If you need to upgrade the major or minor versions, include any of the following parameters:
154+
* --increment-major-version - To increment the major version of package. This will update the version to (N+1).X.X.dev0
155+
* --increment-minor-version - To increment the minor version of package. This will update the version to X.(N+1).X.dev0
156+
* Example: `python3 tools/build_release.py --increment-minor-version`
157+
* If you need to update the version for any specific driver(s), include the `drivers` parameter. By default, all drivers will be considered.
158+
For example:
159+
```bash
160+
python3 tools/build_release.py --drivers nidcpower --increment-minor-version
161+
```
162+
* Commit to branch
152163
1. Update [CHANGELOG.md](./CHANGELOG.md)
153164
* For packages that are releasing:
154165
* Delete empty (i.e. No changes) sub-sections under "Unreleased" section
@@ -157,8 +168,13 @@ Release Process
157168
* Change [Unreleased] in TOC to the version of the release
158169
* Commit to branch
159170
1. Update release versions
160-
* `python3 tools/build_release.py --update --release`
171+
* `python3 tools/build_release.py --update-for-release`
161172
* For each module, this will drop the .devN from our versions in config_addon.py and update the LATEST_RELEASE versions to match.
173+
* If you need to release any specific module(s), include the `drivers` parameter.
174+
For example:
175+
```bash
176+
python3 tools/build_release.py --drivers nidcpower --update-for-release
177+
```
162178
* Commit to branch
163179
1. Clean and build to update generated files with new version
164180
* `python3 tools/build_release.py --build`
@@ -170,19 +186,33 @@ Release Process
170186
1. Wait until the pull request has been approved
171187
1. Upload the releases to PyPI
172188
* `python3 tools/build_release.py --upload`
189+
* If you need to upload any specific module(s), include the `drivers` parameter.
190+
For example:
191+
```bash
192+
python3 tools/build_release.py --drivers nidcpower --upload
193+
```
173194
* You will need to type in your PyPI credentials
174195
1. Merge the pull request to origin/master
175-
1. Create a release on GitHub using the portion from the changelog for this release for the description
176-
* Add the ZIP files under `generated/examples` for each module as a release artifact.
196+
1. For each package released, create a release on GitHub using the module's portion from the changelog for this release for the description
197+
* The release tag should be named as follows: `MODULE_NAME-version`.
198+
* Example: `nidcpower-1.5.0`.
199+
* This tag format allows the individual `Read the Docs` projects to determine whether a release applies to them.
200+
* Add the ZIP files under `generated/examples` for each module (not just the releasing one) as a release artifact.
201+
* Internal test code will only look for the latest release tag and expect it to have examples attached for any module
177202
* This should trigger the [check_latest_release](.github/workflows/check_latest_release.yml) workflow. Check the [results](https://github.com/ni/nimi-python/actions/workflows/check_latest_release.yml) before continuing.
178203
1. Post-Release Steps
179204
1. Create and checkout another branch for post-release changes
180-
1. Update the module versions
181-
* `python3 tools/build_release.py --update`
182-
* This will update the version to X.X.(N+1).dev0
205+
1. Update the module version for a patch version upgrade. This will update the version to X.X.(N+1).dev0
206+
* `python3 tools/build_release.py --increment-patch-version`
207+
* If you need to update any specific module(s), include the `drivers` parameter.
208+
For example:
209+
```bash
210+
python3 tools/build_release.py --drivers nidcpower --increment-patch-version
211+
```
183212
* Commit to branch
184213
1. Clean and build to update generated files with new version
185214
* `python3 tools/build_release.py --build`
215+
* Ensure that all changes made as part of build command are specific to intended drivers.
186216
* Commit to branch
187217
1. Update changelog
188218
* Copy Unreleased section from the bottom of the changelog. Modify the package name in the example and TOC. Paste the modified section at the top of intended package's changelog and add a corresponding link to it in the package's TOC.

build/templates/examples.rst.mako

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,23 @@
2727
2828
with open(f'./src/{module_name}/LATEST_RELEASE') as vf:
2929
latest_release_version = vf.read().strip()
30-
released_zip_url = 'https://github.com/ni/nimi-python/releases/download/{}/{}_examples.zip'.format(latest_release_version, module_name)
31-
32-
example_url_base = 'https://github.com/ni/nimi-python/blob/'
3330
3431
from packaging.version import Version
3532
v = Version(module_version)
3633
34+
# Check if the module name and version match the old tag formatting criteria
35+
use_old_tag_format = (
36+
module_name in ['nidcpower', 'nidigital', 'nidmm', 'nifake', 'niswitch', 'nimodinst', 'nifgen', 'niscope', 'nise', 'nitclk']
37+
and latest_release_version == '1.4.9'
38+
)
39+
40+
if not use_old_tag_format:
41+
latest_release_version = module_name + '-' + latest_release_version
42+
43+
released_zip_url = 'https://github.com/ni/nimi-python/releases/download/{}/{}_examples.zip'.format(latest_release_version, module_name)
44+
45+
example_url_base = 'https://github.com/ni/nimi-python/blob/'
46+
3747
if v.dev is None and v.pre is None:
3848
examples_zip_url_text = '`You can download all {} examples here <{}>`_'.format(module_name, released_zip_url)
3949
example_url_base += latest_release_version
@@ -54,4 +64,4 @@ ${helper.get_rst_header_snippet(os.path.basename(e), '-')}
5464
:encoding: utf8
5565
:caption: `(${os.path.basename(e)}) <${example_url_base}/${e.replace('\\', '/')}>`_
5666

57-
% endfor
67+
% endfor

tools/build_release.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
pp = pprint.PrettyPrinter(indent=4, width=100)
1010

1111
default_python_cmd = ['python']
12-
drivers_to_upload = ['nidcpower', 'nidigital', 'nidmm', 'niswitch', 'nimodinst', 'nifgen', 'niscope', 'nise', 'nitclk']
13-
drivers_to_update = ['nifake'] + drivers_to_upload
1412

1513

1614
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
@@ -23,6 +21,8 @@ class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescri
2321

2422

2523
def main():
24+
drivers_to_update = ['nidcpower', 'nidigital', 'nidmm', 'nifake', 'niswitch', 'nimodinst', 'nifgen', 'niscope', 'nise', 'nitclk']
25+
2626
# Setup the required arguments for this script
2727
usage = """Release script
2828
Prereqs
@@ -33,18 +33,31 @@ def main():
3333
parser = argparse.ArgumentParser(description=usage, formatter_class=CustomFormatter)
3434

3535
build_group = parser.add_argument_group("Build configuration")
36-
build_group.add_argument("--release", action="store_true", default=False, help="This is a release build, so only remove '.devN'. build, then update with .dev0")
3736
build_group.add_argument("--upload", action="store_true", default=False, help="Upload build distributions to PyPI")
38-
build_group.add_argument("--update", action="store_true", default=False, help="Update version in config.py files")
3937
build_group.add_argument("--build", action="store_true", default=False, help="Clean and build")
4038
build_group.add_argument("--python-cmd", action="store", default=None, help=f"Command to use for invoking python. Default: {default_python_cmd}")
39+
build_group.add_argument("--drivers", action="store", default=None, help="Comma-separated list of drivers to update. Default: All Drivers")
40+
build_group.add_argument("--increment-major-version", action="store_true", default=False, help="Increment the major version")
41+
build_group.add_argument("--increment-minor-version", action="store_true", default=False, help="Increment the minor version")
42+
build_group.add_argument("--increment-patch-version", action="store_true", default=False, help="Increment the patch version")
43+
build_group.add_argument("--update-for-release", action="store_true", default=False, help="This is a release build, so only remove '.devN'. build, then update with .dev0")
4144

4245
verbosity_group = parser.add_argument_group("Verbosity, Logging & Debugging")
4346
verbosity_group.add_argument("-v", "--verbose", action="count", default=0, help="Verbose output")
4447
verbosity_group.add_argument("--preview", action="store_true", default=False, help="Show what would happen when running with given parameters")
4548
verbosity_group.add_argument("--log-file", action="store", default=None, help="Send logging to listed file instead of stdout")
4649
args = parser.parse_args()
4750

51+
# Validate that only one of the version-related flags is provided
52+
version_flags = [
53+
args.increment_major_version,
54+
args.increment_minor_version,
55+
args.increment_patch_version,
56+
args.update_for_release,
57+
]
58+
if sum(version_flags) > 1:
59+
raise ValueError("Only one of --increment-major-version, --increment-minor-version, --increment-patch-version or --update-for-release can be provided.")
60+
4861
if args.verbose > 1:
4962
configure_logging(logging.DEBUG, args.log_file)
5063
elif args.verbose == 1:
@@ -64,14 +77,28 @@ def main():
6477
passthrough_params.append('--preview')
6578
if args.log_file:
6679
passthrough_params.append('--log-file').append(args.log_file)
67-
if args.release:
80+
if args.update_for_release:
6881
passthrough_params.append('--release')
69-
70-
if args.update:
82+
if args.increment_patch_version:
83+
passthrough_params.append('--update-type=patch')
84+
if args.increment_minor_version:
85+
passthrough_params.append('--update-type=minor')
86+
if args.increment_major_version:
87+
passthrough_params.append('--update-type=major')
88+
89+
if args.drivers:
90+
provided_drivers = args.drivers.split(",")
91+
invalid_drivers = [driver for driver in provided_drivers if driver not in drivers_to_update]
92+
93+
if invalid_drivers:
94+
raise ValueError(f"The following drivers are invalid: {', '.join(invalid_drivers)}. Valid drivers are: {','.join(drivers_to_update)}")
95+
drivers_to_update = provided_drivers
96+
97+
if any([args.increment_major_version, args.increment_minor_version, args.increment_patch_version, args.update_for_release]):
7198
logging.info('Updating versions')
7299

73100
for d in drivers_to_update:
74-
logging.info(pp.pformat(python_cmd + ['tools/updateReleaseInfo.py', '--src-folder', f'src/{d}', ] + passthrough_params))
101+
logging.info(pp.pformat(python_cmd + ['tools/updateReleaseInfo.py', '--src-folder', f'src/{d}',] + passthrough_params))
75102
check_call(python_cmd + ['tools/updateReleaseInfo.py', '--src-folder', f'src/{d}', ] + passthrough_params)
76103

77104
if args.build:
@@ -86,6 +113,7 @@ def main():
86113
if args.upload:
87114
logging.info('Uploading to PyPI')
88115
complete_twine_cmd = twine_cmd + ['upload']
116+
drivers_to_upload = [driver for driver in drivers_to_update if driver != 'nifake']
89117
for d in drivers_to_upload:
90118
complete_twine_cmd += [f'generated/{d}/dist/*']
91119

tools/updateReleaseInfo.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# !python
2-
31
import argparse
42
from configure_logging import configure_logging
53
import logging
@@ -10,15 +8,32 @@
108
pp = pprint.PrettyPrinter(indent=4, width=100)
119

1210

11+
# Increment version based on bump type ('major', 'minor', 'patch').
12+
def bump_version(version, bump_type):
13+
major, minor, patch = map(int, version.split('.'))
14+
15+
if bump_type == 'patch':
16+
patch += 1
17+
elif bump_type == 'minor':
18+
minor += 1
19+
patch = 0
20+
elif bump_type == 'major':
21+
major += 1
22+
minor = 0
23+
patch = 0
24+
25+
return f"{major}.{minor}.{patch}"
26+
27+
1328
def main():
14-
# Setup the required arguments for this script
1529
usage = """
1630
Update version in files. Example: X.Y.Z.devN to X.Y.Z
1731
"""
1832
parser = argparse.ArgumentParser(description=usage)
1933
file_group = parser.add_argument_group("Input and Output files")
2034
file_group.add_argument("--src-folder", action="store", required=True, help="Source folder")
2135
file_group.add_argument("--release", action="store_true", default=False, help="This is a release build, so only remove '.devN'. Error if not there")
36+
file_group.add_argument("--update-type", action="store", default=None, choices=["major", "minor", "patch"], help="Specify the type of update: major, minor or patch. ")
2237

2338
verbosity_group = parser.add_argument_group("Verbosity, Logging & Debugging")
2439
verbosity_group.add_argument("-v", "--verbose", action="count", default=0, help="Verbose output")
@@ -34,27 +49,37 @@ def main():
3449
configure_logging(logging.WARNING, args.log_file)
3550

3651
logging.info(pp.pformat(args))
37-
3852
metadata_file = os.path.join(args.src_folder, "metadata", "config_addon.py")
3953
with open(metadata_file) as content_file:
4054
contents = content_file.read()
4155

42-
module_dev_version_re = re.compile(r"'module_version': '(\d+\.\d+\.\d+)\.dev(\d+)'")
43-
m = module_dev_version_re.search(contents)
56+
module_version_re = re.compile(r"'module_version': '(\d+\.\d+\.\d+)(?:\.dev(\d+))?'")
57+
m = module_version_re.search(contents)
58+
logging.debug(f"Version regex match: {m}")
59+
4460
if m:
61+
base_version = m.group(1)
62+
dev_number = int(m.group(2)) if m.group(2) else None
63+
64+
if dev_number is not None:
65+
logging.info("Dev version found")
66+
current_version = f"{base_version}.dev{dev_number}"
67+
else:
68+
logging.info("Release version found")
69+
current_version = base_version
70+
4571
if args.release:
46-
logging.info('Dev version found, updating {0}.dev{1} to {0}'.format(m.group(1), int(m.group(2))))
47-
contents = module_dev_version_re.sub(f"'module_version': '{m.group(1)}'", contents)
48-
new_version = m.group(1)
72+
if dev_number is not None:
73+
new_version = base_version
74+
else:
75+
logging.error("Error: Attempting to release an already released version.")
76+
return
4977
else:
50-
logging.info('Dev version found, updating {0}.dev{1} to {0}.dev{2}'.format(m.group(1), int(m.group(2)), int(m.group(2)) + 1))
51-
contents = module_dev_version_re.sub(f"'module_version': '{m.group(1)}.dev{int(m.group(2)) + 1}'", contents)
78+
bumped_version = bump_version(base_version, args.update_type)
79+
new_version = f"{bumped_version}.dev0"
5280

53-
module_version_re = re.compile(r"'module_version': '(\d+\.\d+\.)(\d+)'")
54-
m = module_version_re.search(contents)
55-
if m and not args.release:
56-
logging.info('Release version found, updating {0}{1} to {0}{2}.dev0'.format(m.group(1), int(m.group(2)), int(m.group(2)) + 1))
57-
contents = module_version_re.sub(f"'module_version': '{m.group(1)}{int(m.group(2)) + 1}.dev0'", contents)
81+
logging.info(f"Updating {current_version} to {new_version}")
82+
contents = module_version_re.sub(f"'module_version': '{new_version}'", contents)
5883

5984
if not args.preview:
6085
with open(metadata_file, 'w') as content_file:
@@ -70,4 +95,3 @@ def main():
7095

7196
if __name__ == '__main__':
7297
main()
73-

0 commit comments

Comments
 (0)