From af6707c7e05e2b10a007d368f2d77040f2929ce7 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 14:13:29 -0500 Subject: [PATCH 01/27] Add update-project-version action --- README.md | 91 +++++++++++++++++++++++- update-project-version/action.yml | 54 ++++++++++++++ update-project-version/update_version.py | 61 ++++++++++++++++ 3 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 update-project-version/action.yml create mode 100644 update-project-version/update_version.py diff --git a/README.md b/README.md index 707ccec..9506977 100644 --- a/README.md +++ b/README.md @@ -91,4 +91,93 @@ steps: with: poetry-version: 2.1.3 - run: poetry install -v -``` \ No newline at end of file +``` + +## `ni/python-actions/update-project-version` + +The `update-project-version` action uses Poetry to update the version of a Python project and +creates a pull request to modify its `pyproject.toml` file. + +This action requires Poetry, so you must call `setup-python` and `setup-poetry` first. + +Creating a pull request requires the workflow or job to have the following `GITHUB_TOKEN` +permissions: + +```yaml +permissions: + contents: write + pull-requests: write +```` + +### Usage + +```yaml +steps: +- uses: ni/python-actions/setup-python@v0.1.0 +- uses: ni/python-actions/setup-poetry@v0.1.0 +- uses: ni/python-actions/update-project-version@v0.1.0 +``` + +### Inputs + +#### `project-directory` + +You can specify `project-directory` to update a project located in a subdirectory. + +```yaml +- uses: ni/python-actions/update-project-version@v0.1.0 + with: + project-directory: packages/foo +``` + +#### `branch-prefix` + +You can specify `branch-prefix` to customize the pull request branch names. The default value of +`users/build/` generates pull requests with names like `users/build/update-project-version-main` and +`users/build/update-project-version-releases-1.1`. + +```yaml +- uses: ni/python-actions/update-project-version@v0.1.0 + with: + branch-prefix: users/python-build/ +``` + +#### `create-pull-request` + +You can use `create-pull-request` and `project-directory` to update multiple projects with a single +pull request. + +```yaml +- uses: ni/python-actions/update-project-version@v0.1.0 + with: + project-directory: packages/foo + create-pull-request: false +- uses: ni/python-actions/update-project-version@v0.1.0 + with: + project-directory: packages/bar + create-pull-request: false +- uses: ni/python-actions/update-project-version@v0.1.0 + with: + project-directory: packages/baz + create-pull-request: true +``` + +#### `version-rule` and `use-dev-suffix` + +You can specify `version-rule` and `use-dev-suffix` to customize the versioning scheme. + +- `version-rule` specifies the rule for updating the version number. For example, `major` will + update 1.0.0 -> 2.0.0, while `patch` will update 1.0.0 -> 1.0.1. See [the docs for `poetry + version`](https://python-poetry.org/docs/cli/#version) for the list of rules and their behavior. +- `use-dev-suffix` specifies whether to use development versions like `1.0.0.dev0`. + +The defaults are `version-rule=patch` and `use-dev-suffix=true`, which have the following behavior: + +| Old Version | New Version | +| ----------- | ----------- | +| 1.0.0 | 1.0.1.dev0 | +| 1.0.1.dev0 | 1.0.1.dev1 | +| 1.0.1.dev1 | 1.0.1.dev2 | + +When you are ready to exit the "dev" phase, you should manually update the version number to the +desired release version before creating a release in GitHub. diff --git a/update-project-version/action.yml b/update-project-version/action.yml new file mode 100644 index 0000000..3b8647c --- /dev/null +++ b/update-project-version/action.yml @@ -0,0 +1,54 @@ +name: Update project version +description: Update the version of a Python project and create a pull request. +inputs: + project-directory: + description: Path to the directory containing pyproject.toml. + default: ${{ github.workspace }} + branch-prefix: + description: The branch prefix to use when creating pull requests. + default: users/build/ + create-pull-request: + description: > + Specifies whether to create a pull request. Set this to false to update + the project version without creating a pull request. + default: true + version-rule: + description: > + Specifies the rule for how to update the version number, such as "major", + "minor", or "patch". See https://python-poetry.org/docs/cli/#version for + the list of rules and their behavior. + default: patch + use-dev-suffix: + description: Specifies whether to use development versions like "1.0.0.dev0". + default: true +runs: + using: composite + steps: + - name: Set variables + id: set-vars + run: | + base_branch=$(git symbolic-ref --short HEAD) + base_branch_no_slashes=$(echo $base_branch | tr '/' '-') + echo "base-branch=$base_branch" >> "$GITHUB_OUTPUT" + echo "branch-name=${{ inputs.branch-prefix }}update-project-version-$base_branch_no_slashes" >> "$GITHUB_OUTPUT" + shell: bash + - name: Update the version in pyproject.toml + run: python ${{ github.action_path }}/update_version.py ${{ inputs.version-rule }} ${{ inputs.use-dev-suffix && '--dev' || '' }} + shell: bash + working-directory: ${{ inputs.project-directory }} + - name: Get changed files + id: get-changed-files + run: echo "changed-files=$(git diff --name-only)" >> "$GITHUB_OUTPUT" + shell: bash + - name: Create pull request + if: inputs.create-pull-request + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 + with: + branch: ${{ steps.set-vars.outputs.branch-name }} + title: "chore: Update project version - ${{ steps.set-vars.outputs.base-branch }}" + body: | + This PR updates the versions of the following Python projects: + + ${{ steps.get-changed-files.outputs.changed-files }} + + This PR was generated by [ni/python-actions/update-project-version](https://github.com/ni/python-actions/). \ No newline at end of file diff --git a/update-project-version/update_version.py b/update-project-version/update_version.py new file mode 100644 index 0000000..5d1004b --- /dev/null +++ b/update-project-version/update_version.py @@ -0,0 +1,61 @@ +"""Wrapper for ``poetry version`` that implements the "dev" bump rule. + +This script is a workaround for https://github.com/python-poetry/poetry/issues/8718 - "Add 'dev' as +version bump rule for developmental releases." +""" + +import argparse +import re +import subprocess +import sys + + +def _bump_dev_version(version: str) -> str: + """Bump the "dev" version number, if present. + + >>> _bump_dev_version("1.0.0") + '1.0.0' + >>> _bump_dev_version("1.0.0.dev0") + '1.0.0.dev1' + >>> _bump_dev_version("1.0.0-dev0") + '1.0.0-dev1' + >>> _bump_dev_version("1.0.0_dev0") + '1.0.0_dev1' + >>> _bump_dev_version("1.0.0dev0") + '1.0.0dev1' + >>> _bump_dev_version("1.0.0.dev99") + '1.0.0.dev100' + """ + # Dot, dash, and underscore are all valid. Do not bother normalizing to dot. + match = re.match(r"^(.*[\.-_]?dev)(\d+)$", version) + if match: + version = f"{match.group(1)}{int(match.group(2))+1}" + return version + + +def main(args: list[str]) -> None: + """Main function.""" + parser = argparse.ArgumentParser() + parser.add_argument("rule") + parser.add_argument("--dev", action="store_true") + args = parser.parse_args() + + version = subprocess.check_output(["poetry", "version", "--short"]) + + if args.dev: + if "dev" in version: + new_version = _bump_dev_version() + subprocess.run(["poetry", "version", new_version]) + else: + # Run `poetry version` to update the version using the specified bump rule (e.g. "1.0.0" + # -> "2.0.0", "1.1.0", or "1.0.1"), then add ".dev0" to the end. + subprocess.run(["poetry", "version", args.rule]) + new_version = subprocess.check_output(["poetry", "version", "--short"]) + new_version += ".dev0" + subprocess.run(["poetry", "version", new_version]) + else: + subprocess.run(["poetry", "version", args.rule]) + + +if __name__ == "__main__": + sys.exit(main(sys.args[1:])) From 80b02bdad631b9b0f1af260c5cb048a6aca0754a Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 14:25:28 -0500 Subject: [PATCH 02/27] update-project-version: Get script working --- update-project-version/update_version.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/update-project-version/update_version.py b/update-project-version/update_version.py index 5d1004b..1e6e689 100644 --- a/update-project-version/update_version.py +++ b/update-project-version/update_version.py @@ -40,17 +40,19 @@ def main(args: list[str]) -> None: parser.add_argument("--dev", action="store_true") args = parser.parse_args() - version = subprocess.check_output(["poetry", "version", "--short"]) + version = subprocess.check_output(["poetry", "version", "--short"], text=True).strip() if args.dev: if "dev" in version: - new_version = _bump_dev_version() + new_version = _bump_dev_version(version) subprocess.run(["poetry", "version", new_version]) else: # Run `poetry version` to update the version using the specified bump rule (e.g. "1.0.0" # -> "2.0.0", "1.1.0", or "1.0.1"), then add ".dev0" to the end. subprocess.run(["poetry", "version", args.rule]) - new_version = subprocess.check_output(["poetry", "version", "--short"]) + new_version = subprocess.check_output( + ["poetry", "version", "--short"], text=True + ).strip() new_version += ".dev0" subprocess.run(["poetry", "version", new_version]) else: @@ -58,4 +60,4 @@ def main(args: list[str]) -> None: if __name__ == "__main__": - sys.exit(main(sys.args[1:])) + sys.exit(main(sys.argv[1:])) From 269318aa7aefa0298bfe46f339318502a736235e Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 15:12:32 -0500 Subject: [PATCH 03/27] update-project-version: Add a link to the workflow log. --- update-project-version/action.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index 3b8647c..fbe0f58 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -51,4 +51,5 @@ runs: ${{ steps.get-changed-files.outputs.changed-files }} - This PR was generated by [ni/python-actions/update-project-version](https://github.com/ni/python-actions/). \ No newline at end of file + This PR was generated by [ni/python-actions/update-project-version](https://github.com/ni/python-actions/). + View the [workflow log](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/job/${{ github.job }}). From 11893af3caed70df2fdb9247845c3945c2f742ce Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 15:20:15 -0500 Subject: [PATCH 04/27] update-project-version: Fix handling of Boolean inputs --- update-project-version/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index fbe0f58..d80feb1 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -33,7 +33,7 @@ runs: echo "branch-name=${{ inputs.branch-prefix }}update-project-version-$base_branch_no_slashes" >> "$GITHUB_OUTPUT" shell: bash - name: Update the version in pyproject.toml - run: python ${{ github.action_path }}/update_version.py ${{ inputs.version-rule }} ${{ inputs.use-dev-suffix && '--dev' || '' }} + run: python ${{ github.action_path }}/update_version.py ${{ inputs.version-rule }} ${{ inputs.use-dev-suffix == 'true' && '--dev' || '' }} shell: bash working-directory: ${{ inputs.project-directory }} - name: Get changed files @@ -41,7 +41,7 @@ runs: run: echo "changed-files=$(git diff --name-only)" >> "$GITHUB_OUTPUT" shell: bash - name: Create pull request - if: inputs.create-pull-request + if: inputs.create-pull-request == 'true' && steps.get-changed-files.outputs.changed-files != '' uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: branch: ${{ steps.set-vars.outputs.branch-name }} From 14d54c422e6716fd0e64a17dca2c54ff663a9037 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 15:24:21 -0500 Subject: [PATCH 05/27] update-project-version: Remove newlines from changed-files --- update-project-version/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index d80feb1..188250e 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -38,7 +38,7 @@ runs: working-directory: ${{ inputs.project-directory }} - name: Get changed files id: get-changed-files - run: echo "changed-files=$(git diff --name-only)" >> "$GITHUB_OUTPUT" + run: echo "changed-files=$(git diff --name-only | tr -d '\n')" >> "$GITHUB_OUTPUT" shell: bash - name: Create pull request if: inputs.create-pull-request == 'true' && steps.get-changed-files.outputs.changed-files != '' From 6610715d268951ed3461a6d2455c29dc8ec8b11a Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 15:44:07 -0500 Subject: [PATCH 06/27] update-project-version: Reformat changed-files as a Markdown list --- update-project-version/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index 188250e..b0f198f 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -38,10 +38,10 @@ runs: working-directory: ${{ inputs.project-directory }} - name: Get changed files id: get-changed-files - run: echo "changed-files=$(git diff --name-only | tr -d '\n')" >> "$GITHUB_OUTPUT" + run: echo "changed-files="$(git diff --name-only | awk '{ print "-", $0 }')" >> "$GITHUB_OUTPUT" shell: bash - name: Create pull request - if: inputs.create-pull-request == 'true' && steps.get-changed-files.outputs.changed-files != '' + if: inputs.create-pull-request == 'true' uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: branch: ${{ steps.set-vars.outputs.branch-name }} From 96e22e200ac7b0b29ef1f9ca1d47678fc5e00d22 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 15:48:38 -0500 Subject: [PATCH 07/27] update-project-version: Fix quoting --- update-project-version/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index b0f198f..43d1e58 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -38,7 +38,7 @@ runs: working-directory: ${{ inputs.project-directory }} - name: Get changed files id: get-changed-files - run: echo "changed-files="$(git diff --name-only | awk '{ print "-", $0 }')" >> "$GITHUB_OUTPUT" + run: echo "changed-files="$(git diff --name-only | awk '{ print \"-\", $0 }')" >> "$GITHUB_OUTPUT" shell: bash - name: Create pull request if: inputs.create-pull-request == 'true' From 81b06dd050ce7bce564774d4e81351181989e2b3 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 16:12:15 -0500 Subject: [PATCH 08/27] update-project-version: Fix quoting, take 2 --- update-project-version/action.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index 43d1e58..7eaed1e 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -38,7 +38,11 @@ runs: working-directory: ${{ inputs.project-directory }} - name: Get changed files id: get-changed-files - run: echo "changed-files="$(git diff --name-only | awk '{ print \"-\", $0 }')" >> "$GITHUB_OUTPUT" + run: | + echo "changed-files<> "$GITHUB_OUTPUT" + # Prefix with "- " to generate a Markdown list. + git diff --name-only | awk '{ print "-", $0 }' >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" shell: bash - name: Create pull request if: inputs.create-pull-request == 'true' From 058d8a6e063b7d6cd1b70295e9cff00e0b903cff Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 16:27:57 -0500 Subject: [PATCH 09/27] update-project-version: Fix log URL --- update-project-version/action.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index 7eaed1e..5aafa98 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -44,6 +44,12 @@ runs: git diff --name-only | awk '{ print "-", $0 }' >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" shell: bash + - name: Get the current job's log URL + # Get a log URL like https://github.com/ni/foo/actions/runs/123/job/456. All + # of this information is available in the `github` context except the + # numeric job id at the end, which is only available via the REST API. + id: get-log-url + uses: Tiryoh/gha-jobid-action@be260d8673c9211a84cdcf37794ebd654ba81eef # v1.4.0 - name: Create pull request if: inputs.create-pull-request == 'true' uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 @@ -55,5 +61,4 @@ runs: ${{ steps.get-changed-files.outputs.changed-files }} - This PR was generated by [ni/python-actions/update-project-version](https://github.com/ni/python-actions/). - View the [workflow log](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/job/${{ github.job }}). + This PR was generated by [ni/python-actions/update-project-version](https://github.com/ni/python-actions/). View the [workflow log](${{ steps.get-log-url.outputs.html_url }}). From 62f543d4572d3b3ffa52b2ffccb2e790cb2938a9 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 16:40:00 -0500 Subject: [PATCH 10/27] update-project-version: Fix log URL, take 2 --- update-project-version/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index 5aafa98..c8c8bf9 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -50,6 +50,8 @@ runs: # numeric job id at the end, which is only available via the REST API. id: get-log-url uses: Tiryoh/gha-jobid-action@be260d8673c9211a84cdcf37794ebd654ba81eef # v1.4.0 + with: + job_name: ${{ github.job }} - name: Create pull request if: inputs.create-pull-request == 'true' uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 From c8fe412bd28f7581ed070579e4a22effedf2d712 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 16:51:17 -0500 Subject: [PATCH 11/27] update-project-version: Fix log URL, take 3 --- update-project-version/action.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index c8c8bf9..76268d1 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -43,24 +43,20 @@ runs: # Prefix with "- " to generate a Markdown list. git diff --name-only | awk '{ print "-", $0 }' >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" - shell: bash - - name: Get the current job's log URL - # Get a log URL like https://github.com/ni/foo/actions/runs/123/job/456. All - # of this information is available in the `github` context except the - # numeric job id at the end, which is only available via the REST API. - id: get-log-url - uses: Tiryoh/gha-jobid-action@be260d8673c9211a84cdcf37794ebd654ba81eef # v1.4.0 - with: - job_name: ${{ github.job }} + shell: bash - name: Create pull request if: inputs.create-pull-request == 'true' uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: branch: ${{ steps.set-vars.outputs.branch-name }} title: "chore: Update project version - ${{ steps.set-vars.outputs.base-branch }}" + # The workflow log currently points to the run, not the specific job + # within that run. Linking to a specific job requires the numeric job id, + # which is not available in the github context. + # https://stackoverflow.com/questions/71240338/obtain-job-id-from-a-workflow-run-using-contexts body: | This PR updates the versions of the following Python projects: ${{ steps.get-changed-files.outputs.changed-files }} - This PR was generated by [ni/python-actions/update-project-version](https://github.com/ni/python-actions/). View the [workflow log](${{ steps.get-log-url.outputs.html_url }}). + This PR was generated by [ni/python-actions/update-project-version](https://github.com/ni/python-actions/). View the [workflow log](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). From e183aaeec4ffab2bc331bf47093bccc086bc19d3 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 21 May 2025 17:11:15 -0500 Subject: [PATCH 12/27] README.md: Update version tags to v0.2 --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 9506977..9ea8f13 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ By default, this action installs Python 3.11.9. ```yaml steps: -- uses: ni/python-actions/setup-python@v0.1.0 +- uses: ni/python-actions/setup-python@v0.2 ``` ### Inputs @@ -28,7 +28,7 @@ strategy: matrix: python-version: [3.9, '3.10', 3.11, 3.12, 3.13] steps: -- uses: ni/python-actions/setup-python@v0.1.0 +- uses: ni/python-actions/setup-python@v0.2 with: python-version: ${{ matrix.python-version }} ``` @@ -40,7 +40,7 @@ steps: You can use the `python-version` output to get the actual version of Python, which is useful for caching: ```yaml steps: -- uses: ni/python-actions/setup-python@v0.1.0 +- uses: ni/python-actions/setup-python@v0.2 id: setup-python - uses: actions/cache@v4 with: @@ -56,7 +56,7 @@ containing the Python installation. You can also use the `python-path` output to get the path to the Python interpreter: ```yaml steps: -- uses: ni/python-actions/setup-python@v0.1.0 +- uses: ni/python-actions/setup-python@v0.2 id: setup-python - run: pipx install --python ${{ steps.setup-python.outputs.python-version }} ``` @@ -75,8 +75,8 @@ By default, this action installs Poetry 1.8.2. ```yaml steps: -- uses: ni/python-actions/setup-python@v0.1.0 -- uses: ni/python-actions/setup-poetry@v0.1.0 +- uses: ni/python-actions/setup-python@v0.2 +- uses: ni/python-actions/setup-poetry@v0.2 - run: poetry install -v ``` @@ -86,8 +86,8 @@ steps: ```yaml steps: -- uses: ni/python-actions/setup-python@v0.1.0 -- uses: ni/python-actions/setup-poetry@v0.1.0 +- uses: ni/python-actions/setup-python@v0.2 +- uses: ni/python-actions/setup-poetry@v0.2 with: poetry-version: 2.1.3 - run: poetry install -v @@ -113,9 +113,9 @@ permissions: ```yaml steps: -- uses: ni/python-actions/setup-python@v0.1.0 -- uses: ni/python-actions/setup-poetry@v0.1.0 -- uses: ni/python-actions/update-project-version@v0.1.0 +- uses: ni/python-actions/setup-python@v0.2 +- uses: ni/python-actions/setup-poetry@v0.2 +- uses: ni/python-actions/update-project-version@v0.2 ``` ### Inputs @@ -125,7 +125,7 @@ steps: You can specify `project-directory` to update a project located in a subdirectory. ```yaml -- uses: ni/python-actions/update-project-version@v0.1.0 +- uses: ni/python-actions/update-project-version@v0.2 with: project-directory: packages/foo ``` @@ -137,7 +137,7 @@ You can specify `branch-prefix` to customize the pull request branch names. The `users/build/update-project-version-releases-1.1`. ```yaml -- uses: ni/python-actions/update-project-version@v0.1.0 +- uses: ni/python-actions/update-project-version@v0.2 with: branch-prefix: users/python-build/ ``` @@ -148,15 +148,15 @@ You can use `create-pull-request` and `project-directory` to update multiple pro pull request. ```yaml -- uses: ni/python-actions/update-project-version@v0.1.0 +- uses: ni/python-actions/update-project-version@v0.2 with: project-directory: packages/foo create-pull-request: false -- uses: ni/python-actions/update-project-version@v0.1.0 +- uses: ni/python-actions/update-project-version@v0.2 with: project-directory: packages/bar create-pull-request: false -- uses: ni/python-actions/update-project-version@v0.1.0 +- uses: ni/python-actions/update-project-version@v0.2 with: project-directory: packages/baz create-pull-request: true From 5dde9e3303c370b52b00aa9564a52d75e20c6480 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Thu, 22 May 2025 10:06:11 -0500 Subject: [PATCH 13/27] Add check-project-version action --- README.md | 71 ++++++++++++++++++++++++++++++++ check-project-version/action.yml | 37 +++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 check-project-version/action.yml diff --git a/README.md b/README.md index 9ea8f13..6f3c4a3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,34 @@ `ni/python-actions` is a Git repository containing reusable GitHub Actions for NI Python projects. +## Table of Contents + +- [`ni/python-actions`](#nipython-actions) + - [Table of Contents](#table-of-contents) + - [`ni/python-actions/setup-python`](#nipython-actionssetup-python) + - [Usage](#usage) + - [Inputs](#inputs) + - [`python-version`](#python-version) + - [Outputs](#outputs) + - [`python-version`](#python-version-1) + - [`python-path`](#python-path) + - [`ni/python-actions/setup-poetry`](#nipython-actionssetup-poetry) + - [Usage](#usage-1) + - [Inputs](#inputs-1) + - [`poetry-version`](#poetry-version) + - [`ni/python-actions/check-project-version`](#nipython-actionscheck-project-version) + - [Usage](#usage-2) + - [Inputs](#inputs-2) + - [`project-directory`](#project-directory) + - [`expected-version`](#expected-version) + - [`ni/python-actions/update-project-version`](#nipython-actionsupdate-project-version) + - [Usage](#usage-3) + - [Inputs](#inputs-3) + - [`project-directory`](#project-directory-1) + - [`branch-prefix`](#branch-prefix) + - [`create-pull-request`](#create-pull-request) + - [`version-rule` and `use-dev-suffix`](#version-rule-and-use-dev-suffix) + ## `ni/python-actions/setup-python` The `setup-python` action installs Python and adds it to the PATH. @@ -23,6 +51,7 @@ steps: #### `python-version` You can specify the `python-version` input for testing with multiple versions of Python: + ```yaml strategy: matrix: @@ -38,6 +67,7 @@ steps: #### `python-version` You can use the `python-version` output to get the actual version of Python, which is useful for caching: + ```yaml steps: - uses: ni/python-actions/setup-python@v0.2 @@ -54,6 +84,7 @@ steps: containing the Python installation. You can also use the `python-path` output to get the path to the Python interpreter: + ```yaml steps: - uses: ni/python-actions/setup-python@v0.2 @@ -93,6 +124,46 @@ steps: - run: poetry install -v ``` +## `ni/python-actions/check-project-version` + +The `check-project-version` action uses Poetry to get the version of a Python project and checks +that it matches an expected version. By default, this action checks against `github.ref_name`, which +is the GitHub release tag for GitHub release events. + +This action requires Poetry, so you must call `setup-python` and `setup-poetry` first. + +### Usage + +```yaml +steps: +- uses: ni/python-actions/setup-python@v0.2 +- uses: ni/python-actions/setup-poetry@v0.2 +- uses: ni/python-actions/check-project-version@v0.2 + if: github.event_name == 'release' +``` + +### Inputs + +#### `project-directory` + +You can specify `project-directory` to check a project located in a subdirectory. + +```yaml +- uses: ni/python-actions/check-project-version@v0.2 + with: + project-directory: packages/foo +``` + +#### `expected-version` + +You can specify `expected-version` to check against something other than `github.ref_name`. + +```yaml +- uses: ni/python-actions/check-project-version@v0.2 + with: + expected-version: ${{ steps.get-expected-version.outputs.version }} +``` + ## `ni/python-actions/update-project-version` The `update-project-version` action uses Poetry to update the version of a Python project and diff --git a/check-project-version/action.yml b/check-project-version/action.yml new file mode 100644 index 0000000..cb8104a --- /dev/null +++ b/check-project-version/action.yml @@ -0,0 +1,37 @@ +name: Check project version +description: Check the version of a Python project against an expected version, such as a release tag. +inputs: + project-directory: + description: Path to the directory containing pyproject.toml. + default: ${{ github.workspace }} + expected-version: + description: > + The expected version. By default, this is `github.ref_name`, which is the + tag for a `release` event. If the version has a leading 'v', it will be + stripped. + default: ${{ github.ref_name }} +runs: + using: composite + steps: + - name: Check project version + run: | + expected_version="${{ inputs.expected-version }}" + # Strip the leading 'v', in case this is a GitHub release tag. + expected_version="${expected_version#v}" + project_version="$(poetry version --short)" + if [ x"$project_version" != x"$expected_version" ]; then + echo < Date: Thu, 22 May 2025 10:27:33 -0500 Subject: [PATCH 14/27] check-project-version: Fix here-document indentation --- check-project-version/action.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/check-project-version/action.yml b/check-project-version/action.yml index cb8104a..016cc6d 100644 --- a/check-project-version/action.yml +++ b/check-project-version/action.yml @@ -21,17 +21,16 @@ runs: project_version="$(poetry version --short)" if [ x"$project_version" != x"$expected_version" ]; then echo < Date: Thu, 22 May 2025 10:45:52 -0500 Subject: [PATCH 15/27] check-project-version: Here-doc take 2 --- check-project-version/action.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/check-project-version/action.yml b/check-project-version/action.yml index 016cc6d..3a23832 100644 --- a/check-project-version/action.yml +++ b/check-project-version/action.yml @@ -15,12 +15,12 @@ runs: steps: - name: Check project version run: | + project_version="$(poetry version --short)" expected_version="${{ inputs.expected-version }}" # Strip the leading 'v', in case this is a GitHub release tag. expected_version="${expected_version#v}" - project_version="$(poetry version --short)" - if [ x"$project_version" != x"$expected_version" ]; then - echo < Date: Thu, 22 May 2025 10:54:39 -0500 Subject: [PATCH 16/27] check-project-version: Remove the `exit 1` because I think it's getting in the way of the `::error` annotation --- check-project-version/action.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/check-project-version/action.yml b/check-project-version/action.yml index 3a23832..8348916 100644 --- a/check-project-version/action.yml +++ b/check-project-version/action.yml @@ -34,6 +34,5 @@ runs: if [ x"$project_version" != x"$expected_version" ]; then echo "::error title=Project Version Error::${error_message//'\n'/'%0A'}" - exit 1 fi shell: bash From bf42b85fcc346ca903b36461cdf2b759e30258b0 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Thu, 22 May 2025 11:19:19 -0500 Subject: [PATCH 17/27] check-project-version: Here-doc take 4 --- check-project-version/action.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/check-project-version/action.yml b/check-project-version/action.yml index 8348916..40b6774 100644 --- a/check-project-version/action.yml +++ b/check-project-version/action.yml @@ -32,7 +32,11 @@ runs: Expected version: $expected_version EOF + # Convert newline to %0A so that GitHub includes the entire error message + # in the annotation. + error_message = "$(echo "$error_message" | sed -z 's/\n/%0A/g;s/%0A$/\n/')" + if [ x"$project_version" != x"$expected_version" ]; then - echo "::error title=Project Version Error::${error_message//'\n'/'%0A'}" + echo "::error title=Project Version Error::$error_message" fi shell: bash From c0c221e8459364ac1a1a32560bd21df5ae939d5e Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Thu, 22 May 2025 11:26:28 -0500 Subject: [PATCH 18/27] check-project-version: Add debug echos --- check-project-version/action.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/check-project-version/action.yml b/check-project-version/action.yml index 40b6774..c1d17ec 100644 --- a/check-project-version/action.yml +++ b/check-project-version/action.yml @@ -13,12 +13,16 @@ inputs: runs: using: composite steps: + - run: echo "::notice title=Notice::Test" + shell: bash - name: Check project version run: | + echo "::notice title=Notice::A" project_version="$(poetry version --short)" expected_version="${{ inputs.expected-version }}" # Strip the leading 'v', in case this is a GitHub release tag. expected_version="${expected_version#v}" + echo "::notice title=Notice::B" read -r -d '' error_message < Date: Thu, 22 May 2025 11:36:38 -0500 Subject: [PATCH 19/27] check-project-version: Here-doc take 5 --- check-project-version/action.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/check-project-version/action.yml b/check-project-version/action.yml index c1d17ec..de766ae 100644 --- a/check-project-version/action.yml +++ b/check-project-version/action.yml @@ -24,7 +24,7 @@ runs: expected_version="${expected_version#v}" echo "::notice title=Notice::B" - read -r -d '' error_message < Date: Thu, 22 May 2025 11:40:18 -0500 Subject: [PATCH 20/27] check-project-version: Here-doc take 6 --- check-project-version/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check-project-version/action.yml b/check-project-version/action.yml index de766ae..fd02691 100644 --- a/check-project-version/action.yml +++ b/check-project-version/action.yml @@ -40,7 +40,7 @@ runs: # Convert newline to %0A so that GitHub includes the entire error message # in the annotation. - error_message = "$(echo "$error_message" | sed -z 's/\n/%0A/g;s/%0A$/\n/')" + error_message="$(echo "$error_message" | sed -z 's/\n/%0A/g;s/%0A$/\n/')" echo "::notice title=Notice::D" if [ x"$project_version" != x"$expected_version" ]; then From c09765bfa2f886e2227f6c2525cb006348736349 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Thu, 22 May 2025 11:45:58 -0500 Subject: [PATCH 21/27] check-project-version: Remove debug notices and restore `exit 1` --- check-project-version/action.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/check-project-version/action.yml b/check-project-version/action.yml index fd02691..0f6b342 100644 --- a/check-project-version/action.yml +++ b/check-project-version/action.yml @@ -13,16 +13,12 @@ inputs: runs: using: composite steps: - - run: echo "::notice title=Notice::Test" - shell: bash - name: Check project version run: | - echo "::notice title=Notice::A" project_version="$(poetry version --short)" expected_version="${{ inputs.expected-version }}" # Strip the leading 'v', in case this is a GitHub release tag. expected_version="${expected_version#v}" - echo "::notice title=Notice::B" error_message="$(cat < Date: Thu, 22 May 2025 17:47:35 -0500 Subject: [PATCH 22/27] update-project-version: Try to make release events use the right branch name --- update-project-version/action.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index 76268d1..4cab44b 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -27,7 +27,13 @@ runs: - name: Set variables id: set-vars run: | - base_branch=$(git symbolic-ref --short HEAD) + # For release events, github.ref_name is the release tag (e.g. 1.0.0) and + # github.event.release.target_commitish is the branch/tag/commit that the + # tag was created from (e.g. main). + # + # For workflow_dispatch events, github.ref_name is the branch/tag/commit + # that the workflow was dispatched on. + base_branch="${{ github.event_name == 'release' && github.event.release.target_commitish || github.ref_name }}" base_branch_no_slashes=$(echo $base_branch | tr '/' '-') echo "base-branch=$base_branch" >> "$GITHUB_OUTPUT" echo "branch-name=${{ inputs.branch-prefix }}update-project-version-$base_branch_no_slashes" >> "$GITHUB_OUTPUT" From 89453df56376aeffcb1e6c69388a5b7413c807f9 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Tue, 3 Jun 2025 17:44:15 -0500 Subject: [PATCH 23/27] update-project-version: Specify the base branch for create-pull-request --- update-project-version/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index 4cab44b..e0b4d81 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -54,6 +54,7 @@ runs: if: inputs.create-pull-request == 'true' uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: + base: ${{ steps.set-vars.outputs.base-branch }} branch: ${{ steps.set-vars.outputs.branch-name }} title: "chore: Update project version - ${{ steps.set-vars.outputs.base-branch }}" # The workflow log currently points to the run, not the specific job From 60e04360e22d1ada7616a5a73ce8736887f27bca Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Tue, 3 Jun 2025 19:08:11 -0500 Subject: [PATCH 24/27] update-project-version: Set commit message to the same thing as the PR title --- update-project-version/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/update-project-version/action.yml b/update-project-version/action.yml index e0b4d81..d7f2cba 100644 --- a/update-project-version/action.yml +++ b/update-project-version/action.yml @@ -56,6 +56,7 @@ runs: with: base: ${{ steps.set-vars.outputs.base-branch }} branch: ${{ steps.set-vars.outputs.branch-name }} + commit-message: "chore: Update project version - ${{ steps.set-vars.outputs.base-branch }}" title: "chore: Update project version - ${{ steps.set-vars.outputs.base-branch }}" # The workflow log currently points to the run, not the specific job # within that run. Linking to a specific job requires the numeric job id, From b3e9a55ce7e161360784e35e83f0f46c582d158c Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 4 Jun 2025 16:06:21 -0500 Subject: [PATCH 25/27] github: Add PR/CI workflows to test the actions --- .github/workflows/CI.yml | 14 +++++ .github/workflows/PR.yml | 18 +++++++ .github/workflows/test_actions.yml | 86 ++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 .github/workflows/CI.yml create mode 100644 .github/workflows/PR.yml create mode 100644 .github/workflows/test_actions.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..3019963 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,14 @@ +name: CI + +on: + push: + branches: + - main + - 'releases/**' + workflow_call: + workflow_dispatch: + +jobs: + test_actions: + name: Test actions + uses: ./.github/workflows/test_actions.yml diff --git a/.github/workflows/PR.yml b/.github/workflows/PR.yml new file mode 100644 index 0000000..3d28ed8 --- /dev/null +++ b/.github/workflows/PR.yml @@ -0,0 +1,18 @@ +name: PR + +on: + pull_request: + branches: + - main + - 'releases/**' + workflow_call: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + run_ci: + name: Run CI + uses: ./.github/workflows/CI.yml \ No newline at end of file diff --git a/.github/workflows/test_actions.yml b/.github/workflows/test_actions.yml new file mode 100644 index 0000000..e24c094 --- /dev/null +++ b/.github/workflows/test_actions.yml @@ -0,0 +1,86 @@ +name: Test actions + +on: + workflow_call: + workflow_dispatch: + +jobs: + test_check_project_version: + name: Test check-project-version + runs-on: ubuntu-latest + steps: + - name: Check out repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Set up Python + uses: ./setup-python + - name: Set up Poetry + uses: ./setup-poetry + - name: Create project with version 1.0.0 + run: | + poetry new test-project + poetry version 1.0.0 -C test-project + - name: Check version == 1.0.0 + uses: ./check-project-version + with: + project-directory: test-project + expected-version: 1.0.0 + - name: Create a notice about the expected failure + run: echo "::notice title=Notice::The next step is expected to fail." + - name: Check version == 1.0.1 (expected to fail) + id: expected-failure + continue-on-error: true + uses: ./check-project-version + with: + project-directory: test-project + expected-version: 1.0.1 + - name: Error if the previous step didn't fail + if: steps.expected-failure.conclusion != 'failure' + run: | + echo "::error title=Test Failure::The previous step did not fail as expected." + exit 1 + test_update_project_version: + name: Test update-project-version + runs-on: ubuntu-latest + steps: + - name: Check out repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Set up Python + uses: ./setup-python + - name: Set up Poetry + uses: ./setup-poetry + - name: Create project with version 1.0.0 + run: | + poetry new test-project + poetry version 1.0.0 -C test-project + - name: Update version to 1.0.1 + uses: ./update-project-version + with: + project-directory: test-project + create-pull-request: false + version-rule: patch + use-dev-suffix: false + - name: Check version == 1.0.1 + uses: ./check-project-version + with: + project-directory: test-project + expected-version: 1.0.1 + - name: Update version to 1.0.2.dev0 + uses: ./update-project-version + with: + project-directory: test-project + create-pull-request: false + - name: Check version == 1.0.2.dev0 + uses: ./check-project-version + with: + project-directory: test-project + expected-version: 1.0.2.dev0 + - name: Update version to 1.0.2.dev1 + uses: ./update-project-version + with: + project-directory: test-project + create-pull-request: false + - name: Check version == 1.0.2.dev1 + uses: ./check-project-version + with: + project-directory: test-project + expected-version: 1.0.2.dev1 From c2e74720bbc7eebf775ed6ac2771787d337a82e0 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 4 Jun 2025 16:10:44 -0500 Subject: [PATCH 26/27] check-project-version: Use the project-directory input --- check-project-version/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/check-project-version/action.yml b/check-project-version/action.yml index 0f6b342..8e05163 100644 --- a/check-project-version/action.yml +++ b/check-project-version/action.yml @@ -42,3 +42,4 @@ runs: exit 1 fi shell: bash + working-directory: ${{ inputs.project-directory }} From 66638c72ff8d401addd44e79d09883a102343792 Mon Sep 17 00:00:00 2001 From: Brad Keryan Date: Wed, 4 Jun 2025 16:18:54 -0500 Subject: [PATCH 27/27] github: Use outcome, not conclusion, to check whether a continue-on-error step failed --- .github/workflows/test_actions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_actions.yml b/.github/workflows/test_actions.yml index e24c094..cbe4154 100644 --- a/.github/workflows/test_actions.yml +++ b/.github/workflows/test_actions.yml @@ -34,7 +34,7 @@ jobs: project-directory: test-project expected-version: 1.0.1 - name: Error if the previous step didn't fail - if: steps.expected-failure.conclusion != 'failure' + if: steps.expected-failure.outcome != 'failure' run: | echo "::error title=Test Failure::The previous step did not fail as expected." exit 1