From deb4dbaba3de5396e0eb2389e728d0b1fe702843 Mon Sep 17 00:00:00 2001 From: bernardcooke53 <66492393+bernardcooke53@users.noreply.github.com> Date: Wed, 1 Nov 2023 17:02:47 +0000 Subject: [PATCH 001/167] style: convert formatter from black to ruff (#746) --- .github/workflows/main.yml | 15 +- .github/workflows/pr.yml | 7 +- .pre-commit-config.yaml | 18 +- CHANGELOG.md | 1134 ++++++++--------- README.rst | 5 +- pyproject.toml | 45 +- semantic_release/hvcs/gitea.py | 5 +- semantic_release/hvcs/gitlab.py | 5 +- semantic_release/version/translator.py | 4 +- .../unit/semantic_release/hvcs/test_gitea.py | 4 +- .../unit/semantic_release/hvcs/test_github.py | 19 +- 11 files changed, 647 insertions(+), 614 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e59cde4d0..9f20641e5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -53,7 +53,11 @@ jobs: python -m pip install ".[dev, mypy]" - name: ruff run: | - python -m ruff check . --config pyproject.toml --diff --show-source + python -m ruff check . \ + --config pyproject.toml \ + --diff \ + --show-source \ + --exit-non-zero-on-fix - name: mypy run: | python -m mypy --ignore-missing-imports semantic_release @@ -78,11 +82,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Install Black - run: python -m pip install black + - name: Install Ruff + run: python -m pip install ruff==0.1.2 - - name: Beautify with Black - run: python -m black . + - name: Format + run: | + python -m ruff format . - name: Commit and push changes uses: github-actions-x/commit@v2.9 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 3511c1d46..914b10653 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -50,7 +50,12 @@ jobs: python -m pip install ".[dev, mypy]" - name: ruff run: | - python -m ruff check . --config pyproject.toml --diff --show-source + python -m ruff check . \ + --config pyproject.toml \ + --diff \ + --show-source \ + --exit-non-zero-on-fix + - name: mypy run: python -m mypy --ignore-missing-imports semantic_release diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ce114cb3d..3e24beae3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,15 +30,18 @@ repos: - id: check-ast # Formatters that may modify source files automatically - - repo: https://github.com/psf/black - rev: 23.9.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.2 hooks: - - id: black + - id: ruff-format + name: ruff (format) + args: ["."] + pass_filenames: false - repo: https://github.com/adamchainz/blacken-docs rev: 1.16.0 hooks: - id: blacken-docs - additional_dependencies: ["black==23.7.0"] + additional_dependencies: ["black==23.10.1"] - repo: https://github.com/asottile/pyupgrade rev: v3.15.0 @@ -48,19 +51,20 @@ repos: # Linters and validation - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.292 + rev: v0.1.2 hooks: - id: ruff + name: ruff (lint) args: - "--fix" - "--exit-non-zero-on-fix" - "--statistics" - - "--format=text" + - "--output-format=text" - "." pass_filenames: false - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.5.1" + rev: "v1.6.1" hooks: - id: mypy args: ["--config-file", "pyproject.toml"] diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e40c38db..36ed3a9cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,19 +57,19 @@ GitHub.upload_asset now raises ValueError instead of requests.HTTPError ([`a13a6 * build(deps-dev): bump ruff from 0.0.286 to 0.0.290 (#713) -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.286 to 0.0.290. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.286...v0.0.290) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.286 to 0.0.290. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.286...v0.0.290) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`6b288f2`](https://github.com/python-semantic-release/python-semantic-release/commit/6b288f2033366a8a2d6e730938df3606fa5ca5a7)) ### Fix @@ -83,12 +83,12 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 (#704) -* build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 - -* build(deps-dev): relax importlib-resources requirement - ---------- - +* build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 + +* build(deps-dev): relax importlib-resources requirement + +--------- + Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`861fe01`](https://github.com/python-semantic-release/python-semantic-release/commit/861fe0119131f69761ae3e06ec46d62e526269d6)) ### Chore @@ -134,50 +134,50 @@ Co-authored-by: github-actions <action@github.com> ([`8a515ca`](https://gi * build(deps-dev): update pytest-xdist requirement (#677) -Updates the requirements on [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) to permit the latest version. -- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst) -- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.0.0...v3.3.1) - ---- -updated-dependencies: -- dependency-name: pytest-xdist - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) to permit the latest version. +- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst) +- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.0.0...v3.3.1) + +--- +updated-dependencies: +- dependency-name: pytest-xdist + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`9ec6321`](https://github.com/python-semantic-release/python-semantic-release/commit/9ec63215c8d60230ff90c06096d8f37956bd156a)) * build(deps-dev): update coverage[toml] requirement (#678) -Updates the requirements on [coverage[toml]](https://github.com/nedbat/coveragepy) to permit the latest version. -- [Release notes](https://github.com/nedbat/coveragepy/releases) -- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) -- [Commits](https://github.com/nedbat/coveragepy/compare/6.0...7.3.0) - ---- -updated-dependencies: -- dependency-name: coverage[toml] - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [coverage[toml]](https://github.com/nedbat/coveragepy) to permit the latest version. +- [Release notes](https://github.com/nedbat/coveragepy/releases) +- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) +- [Commits](https://github.com/nedbat/coveragepy/compare/6.0...7.3.0) + +--- +updated-dependencies: +- dependency-name: coverage[toml] + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`3561a97`](https://github.com/python-semantic-release/python-semantic-release/commit/3561a977fc56ec4bb12c9a894c0072cd8a3519fa)) * build(deps-dev): bump responses from 0.21.0 to 0.23.3 (#680) -Bumps [responses](https://github.com/getsentry/responses) from 0.21.0 to 0.23.3. -- [Release notes](https://github.com/getsentry/responses/releases) -- [Changelog](https://github.com/getsentry/responses/blob/master/CHANGES) -- [Commits](https://github.com/getsentry/responses/compare/0.21.0...0.23.3) - ---- -updated-dependencies: -- dependency-name: responses - dependency-type: direct:production - update-type: version-update:semver-minor -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [responses](https://github.com/getsentry/responses) from 0.21.0 to 0.23.3. +- [Release notes](https://github.com/getsentry/responses/releases) +- [Changelog](https://github.com/getsentry/responses/blob/master/CHANGES) +- [Commits](https://github.com/getsentry/responses/compare/0.21.0...0.23.3) + +--- +updated-dependencies: +- dependency-name: responses + dependency-type: direct:production + update-type: version-update:semver-minor +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`dd6946c`](https://github.com/python-semantic-release/python-semantic-release/commit/dd6946cd479f646f3af028b92ca83db8fc4c0c66)) ### Chore @@ -207,8 +207,8 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * fix: don't warn about vcs token if ignore_token_for_push is true. (#670) -* fix: don't warn about vcs token if ignore_token_for_push is true. - +* fix: don't warn about vcs token if ignore_token_for_push is true. + * docs: `password` should be `token`. ([`f1a54a6`](https://github.com/python-semantic-release/python-semantic-release/commit/f1a54a6c9a05b225b6474d50cd610eca19ec0c34)) ### Style @@ -274,184 +274,184 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * feat!: v8 (#619) -* feat!: 8.0.x (#538) - -Co-authored-by: Johan <johanhmr@gmail.com> -Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> - -* fix: correct Dockerfile CLI command and GHA fetch - -* fix: resolve branch checkout logic in GHA - -* fix: remove commit amending behaviour - -this was not working when there were no source code changes to be made, as it lead -to attempting to amend a HEAD commit that wasn't produced by PSR - -* 8.0.0-alpha.1 - -Automatically generated by python-semantic-release - -* fix: correct logic for generating release notes (#550) - -* fix: cleanup comments and unused logic - -* fix(action): mark container fs as safe for git to operate on - -* style: beautify 49080c510a68cccd2f6c7a8d540b483751901207 - -* fix(action): quotation for git config command - -* 8.0.0-alpha.2 - -Automatically generated by python-semantic-release - -* fix: resolve bug in changelog logic, enable upload to pypi - -* 8.0.0-alpha.3 - -Automatically generated by python-semantic-release - -* test: add tests for ReleaseHistory.release - -* fix: resolve loss of tag_format configuration - -* 8.0.0-alpha.4 - -Automatically generated by python-semantic-release - -* feat: various improvements - -* Added sorting to test parameterisation, so that pytest-xdist works again - dramatic speedup for testing -* Reworked the CI verification code so it's a bit prettier -* Added more testing for the version CLI command, and split some logic out of the command itself -* Removed a redundant double-regex match in VersionTranslator and Version, for some speedup - -* chore(test): proper env patching for tests in CI - -* style: beautify bcb27a4a8ce4789d083226f088c1810f45cd4c77 - -* refactor!: remove verify-ci command - -* 8.0.0-alpha.5 - -Automatically generated by python-semantic-release - -* fix(docs): fixup docs and remove reference to dist publication - -* feat!: remove publication of dists to artefact repository - -* feat: rename 'upload' configuration section to 'publish' - -* feat!: removed build status checking - -* feat: add GitHub Actions output - -* fix(action): remove default for 'force' - -* fix(ci): different workflow for v8 - -* fix(action): correct input parsing - -* fix: correct handling of build commands - -* feat: make it easier to access commit messages in ParsedCommits - -* fix: make additional attributes available for template authors - -* fix: add logging for token auth, use token for push - -* ci: add verbosity - -* fix: caching for repo owner and name - -* ci: contents permission for workflow - -* 8.0.0-alpha.6 - -Automatically generated by python-semantic-release - -* docs: update docs with additional required permissions - -* feat: add option to specify tag to publish to in publish command - -* feat: add Strict Mode - -* docs: convert to Furo theme - -* feat: add --skip-build option - -* 8.0.0-alpha.7 - -Automatically generated by python-semantic-release - -* test: separate command line tests by stdout and stderr - -* ci: pass tag output and conditionally execute publish steps - -* fix: correct assets type in configuration (#603) - -* change raw config assets type - -* fix: correct assets type-annotation for RuntimeContext - ---------- - -Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> - -* 8.0.0-alpha.8 - -Automatically generated by python-semantic-release - -* fix: pin Debian version in Dockerfile - -* feat: promote to rc - -* 8.0.0-rc.1 - -Automatically generated by python-semantic-release - -* ci: fix conditionals in workflow and update documentation - -* ci: correct conditionals - -* fix: only call Github Action output callback once defaults are set - -* 8.0.0-rc.2 - -Automatically generated by python-semantic-release - -* fix: create_or_update_release for Gitlab hvcs - -* ci: remove separate v8 workflow - -* chore: tweak issue templates - -* chore: bump docs dependencies - -* 8.0.0-rc.3 - -Automatically generated by python-semantic-release - -* fix(deps): add types-click, and downgrade sphinx/furo for 3.7 - -* 8.0.0-rc.4 - -Automatically generated by python-semantic-release - -* docs: fix typo (#623) - -* docs: correct typo in docs/changelog_templates.rst - -Co-authored-by: Micael Jarniac <micael@jarniac.com> - ---------- - -Co-authored-by: Johan <johanhmr@gmail.com> -Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> -Co-authored-by: semantic-release <semantic-release> -Co-authored-by: github-actions <action@github.com> -Co-authored-by: smeng9 <38666763+smeng9@users.noreply.github.com> +* feat!: 8.0.x (#538) + +Co-authored-by: Johan <johanhmr@gmail.com> +Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> + +* fix: correct Dockerfile CLI command and GHA fetch + +* fix: resolve branch checkout logic in GHA + +* fix: remove commit amending behaviour + +this was not working when there were no source code changes to be made, as it lead +to attempting to amend a HEAD commit that wasn't produced by PSR + +* 8.0.0-alpha.1 + +Automatically generated by python-semantic-release + +* fix: correct logic for generating release notes (#550) + +* fix: cleanup comments and unused logic + +* fix(action): mark container fs as safe for git to operate on + +* style: beautify 49080c510a68cccd2f6c7a8d540b483751901207 + +* fix(action): quotation for git config command + +* 8.0.0-alpha.2 + +Automatically generated by python-semantic-release + +* fix: resolve bug in changelog logic, enable upload to pypi + +* 8.0.0-alpha.3 + +Automatically generated by python-semantic-release + +* test: add tests for ReleaseHistory.release + +* fix: resolve loss of tag_format configuration + +* 8.0.0-alpha.4 + +Automatically generated by python-semantic-release + +* feat: various improvements + +* Added sorting to test parameterisation, so that pytest-xdist works again - dramatic speedup for testing +* Reworked the CI verification code so it's a bit prettier +* Added more testing for the version CLI command, and split some logic out of the command itself +* Removed a redundant double-regex match in VersionTranslator and Version, for some speedup + +* chore(test): proper env patching for tests in CI + +* style: beautify bcb27a4a8ce4789d083226f088c1810f45cd4c77 + +* refactor!: remove verify-ci command + +* 8.0.0-alpha.5 + +Automatically generated by python-semantic-release + +* fix(docs): fixup docs and remove reference to dist publication + +* feat!: remove publication of dists to artefact repository + +* feat: rename 'upload' configuration section to 'publish' + +* feat!: removed build status checking + +* feat: add GitHub Actions output + +* fix(action): remove default for 'force' + +* fix(ci): different workflow for v8 + +* fix(action): correct input parsing + +* fix: correct handling of build commands + +* feat: make it easier to access commit messages in ParsedCommits + +* fix: make additional attributes available for template authors + +* fix: add logging for token auth, use token for push + +* ci: add verbosity + +* fix: caching for repo owner and name + +* ci: contents permission for workflow + +* 8.0.0-alpha.6 + +Automatically generated by python-semantic-release + +* docs: update docs with additional required permissions + +* feat: add option to specify tag to publish to in publish command + +* feat: add Strict Mode + +* docs: convert to Furo theme + +* feat: add --skip-build option + +* 8.0.0-alpha.7 + +Automatically generated by python-semantic-release + +* test: separate command line tests by stdout and stderr + +* ci: pass tag output and conditionally execute publish steps + +* fix: correct assets type in configuration (#603) + +* change raw config assets type + +* fix: correct assets type-annotation for RuntimeContext + +--------- + +Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> + +* 8.0.0-alpha.8 + +Automatically generated by python-semantic-release + +* fix: pin Debian version in Dockerfile + +* feat: promote to rc + +* 8.0.0-rc.1 + +Automatically generated by python-semantic-release + +* ci: fix conditionals in workflow and update documentation + +* ci: correct conditionals + +* fix: only call Github Action output callback once defaults are set + +* 8.0.0-rc.2 + +Automatically generated by python-semantic-release + +* fix: create_or_update_release for Gitlab hvcs + +* ci: remove separate v8 workflow + +* chore: tweak issue templates + +* chore: bump docs dependencies + +* 8.0.0-rc.3 + +Automatically generated by python-semantic-release + +* fix(deps): add types-click, and downgrade sphinx/furo for 3.7 + +* 8.0.0-rc.4 + +Automatically generated by python-semantic-release + +* docs: fix typo (#623) + +* docs: correct typo in docs/changelog_templates.rst + +Co-authored-by: Micael Jarniac <micael@jarniac.com> + +--------- + +Co-authored-by: Johan <johanhmr@gmail.com> +Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> +Co-authored-by: semantic-release <semantic-release> +Co-authored-by: github-actions <action@github.com> +Co-authored-by: smeng9 <38666763+smeng9@users.noreply.github.com> Co-authored-by: Micael Jarniac <micael@jarniac.com> ([`ec30564`](https://github.com/python-semantic-release/python-semantic-release/commit/ec30564b4ec732c001d76d3c09ba033066d2b6fe)) @@ -475,11 +475,11 @@ Co-authored-by: Micael Jarniac <micael@jarniac.com> ([`ec30564`](https://g * fix: docker build fails installing git (#605) -git was installed from bullseye-backports, but base image is referencing latest python:3.10 -since bookworm was recently released, this now points at bookworm and installing the backport of git is actually trying to downgrade, resulting in this error: - -> E: Packages were downgraded and -y was used without --allow-downgrades. - +git was installed from bullseye-backports, but base image is referencing latest python:3.10 +since bookworm was recently released, this now points at bookworm and installing the backport of git is actually trying to downgrade, resulting in this error: + +> E: Packages were downgraded and -y was used without --allow-downgrades. + > ERROR: failed to solve: process "/bin/sh -c echo \"deb http://deb.debian.org/debian bullseye-backports main\" >> /etc/apt/sources.list; apt-get update; apt-get install -y git/bullseye-backports" did not complete successfully: exit code: 100 ([`9e3eb97`](https://github.com/python-semantic-release/python-semantic-release/commit/9e3eb979783bc39ca564c2967c6c77eecba682e6)) @@ -505,7 +505,7 @@ In #594, I missed that there are 2 places where the version header is formatted * fix: generate markdown linter compliant changelog headers & lists (#594) -Add an extra new line after each header and between sections to fix 2 markdownlint errors +Add an extra new line after each header and between sections to fix 2 markdownlint errors for changelogs generated by this package ([`9d9d403`](https://github.com/python-semantic-release/python-semantic-release/commit/9d9d40305c499c907335abe313e3ed122db0b154)) ### Style @@ -519,9 +519,9 @@ for changelogs generated by this package ([`9d9d403`](https://github.com/python- * feat: add option to only parse commits for current working directory (#509) -When running the application from a sub-directory in the VCS, the option -use_only_cwd_commits will filter out commits that does not changes the -current working directory, similar to running commands like +When running the application from a sub-directory in the VCS, the option +use_only_cwd_commits will filter out commits that does not changes the +current working directory, similar to running commands like `git log -- .` in a sub-directory. ([`cdf8116`](https://github.com/python-semantic-release/python-semantic-release/commit/cdf8116c1e415363b10a01f541873e04ad874220)) @@ -531,22 +531,22 @@ current working directory, similar to running commands like * docs: update broken badge and add links (#591) -The "Test Status" badge was updated to address a recent breaking change in the -GitHub actions API. All the badges updated to add links to the appropriate +The "Test Status" badge was updated to address a recent breaking change in the +GitHub actions API. All the badges updated to add links to the appropriate resources for end-user convenience. ([`0c23447`](https://github.com/python-semantic-release/python-semantic-release/commit/0c234475d27ad887b19170c82deb80293b3a95f1)) ### Fix * fix: update docs and default config for gitmoji changes (#590) -* fix: update docs and default config for gitmoji changes - -PR #582 updated to the latest Gitmojis release however the documentation and -default config options still referenced old and unsupported Gitmojis. - -* fix: update sphinx dep - -I could only build the documentation locally by updating Sphinx to the latest +* fix: update docs and default config for gitmoji changes + +PR #582 updated to the latest Gitmojis release however the documentation and +default config options still referenced old and unsupported Gitmojis. + +* fix: update sphinx dep + +I could only build the documentation locally by updating Sphinx to the latest 1.x version. ([`192da6e`](https://github.com/python-semantic-release/python-semantic-release/commit/192da6e1352298b48630423d50191070a1c5ab24)) @@ -573,16 +573,16 @@ In order to avoid 'Repository not found: relekang/python-semantic-release.&# * docs: spelling and grammar in `travis.rst` (#556) -- spelling -- subject-verb agreement -- remove verbiage - +- spelling +- subject-verb agreement +- remove verbiage + Signed-off-by: Vladislav Doster <mvdoster@gmail.com> ([`3a76e9d`](https://github.com/python-semantic-release/python-semantic-release/commit/3a76e9d7505c421009eb3e953c32cccac2e70e07)) * docs: grammar in `docs/troubleshooting.rst` (#557) -- change contraction to a possessive pronoun - +- change contraction to a possessive pronoun + Signed-off-by: Vladislav Doster <mvdoster@gmail.com> ([`bbe754a`](https://github.com/python-semantic-release/python-semantic-release/commit/bbe754a3db9ce7132749e7902fe118b52f48ee42)) ### Fix @@ -630,11 +630,11 @@ It is spamming to much. We can bring it back if we get the time to fix the spamm * ci: remove python3.6 from GHA, add python3.10 and python3.11 (#541) -* ci: remove python3.6 from GHA, add python3.10 and python3.11 - -GHA workflows are failing without this, due to -https://github.com/actions/setup-python/issues/544\#issuecomment-1332535877 - +* ci: remove python3.6 from GHA, add python3.10 and python3.11 + +GHA workflows are failing without this, due to +https://github.com/actions/setup-python/issues/544\#issuecomment-1332535877 + * fix: upgrade pytest ([`8e4aa0e`](https://github.com/python-semantic-release/python-semantic-release/commit/8e4aa0e30438291ade98604a18aeb372f0d0b52f)) ### Feature @@ -653,17 +653,17 @@ Co-authored-by: Kevin Watson <Kevmo92@users.noreply.github.com> ([`4664afe * fix: changelog release commit search logic (#530) -* Fixes changelog release commit search logic - -Running `semantic-release changelog` currently fails to identify "the last commit in [a] release" because the compared commit messages have superfluous whitespace. -Likely related to the issue causing: https://github.com/relekang/python-semantic-release/issues/490 - +* Fixes changelog release commit search logic + +Running `semantic-release changelog` currently fails to identify "the last commit in [a] release" because the compared commit messages have superfluous whitespace. +Likely related to the issue causing: https://github.com/relekang/python-semantic-release/issues/490 + * Removes a couple of extra `strip()`s. ([`efb3410`](https://github.com/python-semantic-release/python-semantic-release/commit/efb341036196c39b4694ca4bfa56c6b3e0827c6c)) * fix: bump Dockerfile to use Python 3.10 image (#536) -Fixes #533 - +Fixes #533 + Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`8f2185d`](https://github.com/python-semantic-release/python-semantic-release/commit/8f2185d570b3966b667ac591ae523812e9d2e00f)) * fix: fix mypy errors for publish ([`b40dd48`](https://github.com/python-semantic-release/python-semantic-release/commit/b40dd484387c1b3f78df53ee2d35e281e8e799c8)) @@ -689,10 +689,10 @@ Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`8f2185d`](http * ci: Update deprecated actions (#511) -* ci: update depreated actions - -* ci: replace deprecated set-output in workflow - +* ci: update depreated actions + +* ci: replace deprecated set-output in workflow + According to https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ ([`bb09233`](https://github.com/python-semantic-release/python-semantic-release/commit/bb09233b84d153a15784fdf68d7274c9d682c336)) ### Documentation @@ -863,9 +863,9 @@ Fixes #473 ([`0ece6f2`](https://github.com/python-semantic-release/python-semant * fix: allow changing prerelease tag using CLI flags (#466) -Delay construction of version and release patterns until runtime. -This will allow to use non-default prerelease tag. - +Delay construction of version and release patterns until runtime. +This will allow to use non-default prerelease tag. + Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`395bf4f`](https://github.com/python-semantic-release/python-semantic-release/commit/395bf4f2de73663c070f37cced85162d41934213)) @@ -875,8 +875,8 @@ Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`395bf4f`](http * fix(publish): get version bump for current release (#467) -Replicate the behavior of "version" command in version calculation. - +Replicate the behavior of "version" command in version calculation. + Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`dd26888`](https://github.com/python-semantic-release/python-semantic-release/commit/dd26888a923b2f480303c19f1916647de48b02bf)) * fix: add packaging module requirement (#469) ([`b99c9fa`](https://github.com/python-semantic-release/python-semantic-release/commit/b99c9fa88dc25e5ceacb131cd93d9079c4fb2c86)) @@ -950,33 +950,33 @@ Co-authored-by: Sebastian Seith <sebastian@vermill.io> ([`da0606f`](https: * ci: adjust actions test phase to use fetch-depth: 0 to fix ci tests (#446) -Co-authored-by: Sebastian Seith <sebastian@vermill.io> +Co-authored-by: Sebastian Seith <sebastian@vermill.io> Co-authored-by: github-actions <action@github.com> ([`3329eef`](https://github.com/python-semantic-release/python-semantic-release/commit/3329eeffb077f628e4a965bc7fd922d09d6b63da)) ### Feature * feat: allow using ssh-key to push version while using token to publish to hvcs (#419) -* feat(config): add ignore_token_for_push param - -Add ignore_token_for_push parameter that allows using the underlying -git authentication mechanism for pushing a new version commit and tags -while also using an specified token to upload dists - -* test(config): add test for ignore_token_for_push - -Test push_new_version with token while ignore_token_for_push is True -and False - -* docs: add documentation for ignore_token_for_push - -* fix(test): override GITHUB_ACTOR env - -push_new_version is using GITHUB_ACTOR env var but we did not -contemplate in our new tests that actually Github actions running the -tests will populate that var and change the test outcome - -Now we control the value of that env var and test for it being present +* feat(config): add ignore_token_for_push param + +Add ignore_token_for_push parameter that allows using the underlying +git authentication mechanism for pushing a new version commit and tags +while also using an specified token to upload dists + +* test(config): add test for ignore_token_for_push + +Test push_new_version with token while ignore_token_for_push is True +and False + +* docs: add documentation for ignore_token_for_push + +* fix(test): override GITHUB_ACTOR env + +push_new_version is using GITHUB_ACTOR env var but we did not +contemplate in our new tests that actually Github actions running the +tests will populate that var and change the test outcome + +Now we control the value of that env var and test for it being present or not ([`7b2dffa`](https://github.com/python-semantic-release/python-semantic-release/commit/7b2dffadf43c77d5e0eea307aefcee5c7744df5c)) ### Fix @@ -1014,16 +1014,16 @@ Fixes #354 ([`cf74339`](https://github.com/python-semantic-release/python-semant * chore(dependencies): unpin tomlkit dependency (#429) -- tests for a tomlkit regression don't fail anymore with newer tomlkit -- keep comment in setup.py about tomlkit being pinned at some point in time - +- tests for a tomlkit regression don't fail anymore with newer tomlkit +- keep comment in setup.py about tomlkit being pinned at some point in time + refs #336 ([`8515879`](https://github.com/python-semantic-release/python-semantic-release/commit/85158798ca438c1dafc84036d13c2988c934f02f)) ### Fix * fix(prerelase): pass prerelease option to get_current_version (#432) -The `get_current_version` function accepts a `prerelease` argument which +The `get_current_version` function accepts a `prerelease` argument which was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python-semantic-release/commit/aabab0b7ce647d25e0c78ae6566f1132ece9fcb9)) ### Style @@ -1037,7 +1037,7 @@ was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python * chore(dependencies): extend allowed version range for python-gitlab (#417) -* chore(dependencies): extend allowed version range for python-gitlab +* chore(dependencies): extend allowed version range for python-gitlab * fix(type): ignore mypy errors for dynamic RESTObject ([`8ee4d4b`](https://github.com/python-semantic-release/python-semantic-release/commit/8ee4d4b8dabfa5c6cd2aa6180d4a8da8f3c9554c)) ### Feature @@ -1051,18 +1051,18 @@ was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python * feat: add prerelease functionality (#413) -* chore: add initial todos -* feat: add prerelease tag option -* feat: add prerelease cli flag -* feat: omit_pattern for previouse and current version getters -* feat: print_version with prerelease bump -* feat: make print_version prerelease ready -* feat: move prerelease determination to get_new_version -* test: improve get_last_version test -* docs: added basic infos about prereleases -* feat: add prerelease flag to version and publish -* feat: remove leftover todos - +* chore: add initial todos +* feat: add prerelease tag option +* feat: add prerelease cli flag +* feat: omit_pattern for previouse and current version getters +* feat: print_version with prerelease bump +* feat: make print_version prerelease ready +* feat: move prerelease determination to get_new_version +* test: improve get_last_version test +* docs: added basic infos about prereleases +* feat: add prerelease flag to version and publish +* feat: remove leftover todos + Co-authored-by: Mario Jäckle <m.jaeckle@careerpartner.eu> ([`7064265`](https://github.com/python-semantic-release/python-semantic-release/commit/7064265627a2aba09caa2873d823b594e0e23e77)) ### Style @@ -1083,7 +1083,7 @@ Co-authored-by: Mario Jäckle <m.jaeckle@careerpartner.eu> ([`7064265`](ht * fix(gitea): build status and asset upload (#420) -* fix(gitea): handle list build status response +* fix(gitea): handle list build status response * fix(gitea): use form-data for upload_asset ([`57db81f`](https://github.com/python-semantic-release/python-semantic-release/commit/57db81f4c6b96da8259e3bad9137eaccbcd10f6e)) ### Style @@ -1215,10 +1215,10 @@ executable. No-op mode now respected by artifact upload. ([`cfb20af`](https://gi * feat: allow custom environment variable names (#392) -* GH_TOKEN can now be customized by setting github_token_var -* GL_TOKEN can now be customized by setting gitlab_token_var -* PYPI_PASSWORD can now be customized by setting pypi_pass_var -* PYPI_TOKEN can now be customized by setting pypi_token_var +* GH_TOKEN can now be customized by setting github_token_var +* GL_TOKEN can now be customized by setting gitlab_token_var +* PYPI_PASSWORD can now be customized by setting pypi_pass_var +* PYPI_TOKEN can now be customized by setting pypi_token_var * PYPI_USERNAME can now be customized by setting pypi_user_var ([`372cda3`](https://github.com/python-semantic-release/python-semantic-release/commit/372cda3497f16ead2209e6e1377d38f497144883)) * feat: use gitlab-ci or github actions env vars @@ -1286,8 +1286,8 @@ https://github.com/relekang/python-semantic-release/issues/391#issuecomment-9506 * feat: custom git tag format support (#373) -* feat: custom git tag format support -* test: add git tag format check +* feat: custom git tag format support +* test: add git tag format check * docs: add tag_format config option ([`1d76632`](https://github.com/python-semantic-release/python-semantic-release/commit/1d76632043bf0b6076d214a63c92013624f4b95e)) @@ -1333,9 +1333,9 @@ Co-authored-by: Laercio Barbosa <laercio.barbosa@scania.com> ([`a275a7a`]( * chore: bump responses to latest version (#343) -The current version has a Deprecation warning: - -inspect.getargspec() is deprecated since Python 3.0, +The current version has a Deprecation warning: + +inspect.getargspec() is deprecated since Python 3.0, use inspect.signature() or inspect.getfullargspec() ([`e953157`](https://github.com/python-semantic-release/python-semantic-release/commit/e953157125f4528759453218f75b6e51cafd2cc3)) ### Ci @@ -1518,23 +1518,23 @@ Fixes #329 ([`abfacc4`](https://github.com/python-semantic-release/python-semant * docs: correct casing on proper nouns (#320) -* docs: correcting Semantic Versioning casing - -Semantic Versioning is the name of the specification. -Therefore it is a proper noun. -This patch corrects the incorrect casing for Semantic Versioning. - -* docs: correcting Python casing - +* docs: correcting Semantic Versioning casing + +Semantic Versioning is the name of the specification. +Therefore it is a proper noun. +This patch corrects the incorrect casing for Semantic Versioning. + +* docs: correcting Python casing + This patch corrects the incorrect casing for Python. ([`d51b999`](https://github.com/python-semantic-release/python-semantic-release/commit/d51b999a245a4e56ff7a09d0495c75336f2f150d)) ### Feature * feat(checks): add support for Jenkins CI (#322) -Includes a ci check handler to verify jenkins. -Unlike other ci systems jenkins doesn't generally prefix things with -`JENKINS` or simply inject `JENKINS=true` Really the only thing that is +Includes a ci check handler to verify jenkins. +Unlike other ci systems jenkins doesn't generally prefix things with +`JENKINS` or simply inject `JENKINS=true` Really the only thing that is immediately identifiable is `JENKINS_URL` ([`3e99855`](https://github.com/python-semantic-release/python-semantic-release/commit/3e99855c6bc72b3e9a572c58cc14e82ddeebfff8)) ### Style @@ -1552,10 +1552,10 @@ immediately identifiable is `JENKINS_URL` ([`3e99855`](https://github.com/python * fix: fix crash when TOML has no PSR section (#319) -* test: reproduce issue with TOML without PSR section - -* fix: crash when TOML has no PSR section - +* test: reproduce issue with TOML without PSR section + +* fix: crash when TOML has no PSR section + * chore: remove unused imports ([`5f8ab99`](https://github.com/python-semantic-release/python-semantic-release/commit/5f8ab99bf7254508f4b38fcddef2bdde8dd15a4c)) @@ -1578,26 +1578,26 @@ Fixes #306 ([`1a85af4`](https://github.com/python-semantic-release/python-semant * feat: support toml files for version declaration (#307) -This introduce a new `version_toml` configuration property that behaves -like `version_pattern` and `version_variable`. - -For poetry support, user should now set `version_toml = pyproject.toml:tool.poetry.version`. - -This introduce an ABC class, `VersionDeclaration`, that -can be implemented to add other version declarations with ease. - -`toml` dependency has been replaced by `tomlkit`, as this is used -the library used by poetry to generate the `pyproject.toml` file, and -is able to keep the ordering of data defined in the file. - -Existing `VersionPattern` class has been renamed to -`PatternVersionDeclaration` and now implements `VersionDeclaration`. - -`load_version_patterns()` function has been renamed to -`load_version_declarations()` and now return a list of -`VersionDeclaration` implementations. - -Close #245 +This introduce a new `version_toml` configuration property that behaves +like `version_pattern` and `version_variable`. + +For poetry support, user should now set `version_toml = pyproject.toml:tool.poetry.version`. + +This introduce an ABC class, `VersionDeclaration`, that +can be implemented to add other version declarations with ease. + +`toml` dependency has been replaced by `tomlkit`, as this is used +the library used by poetry to generate the `pyproject.toml` file, and +is able to keep the ordering of data defined in the file. + +Existing `VersionPattern` class has been renamed to +`PatternVersionDeclaration` and now implements `VersionDeclaration`. + +`load_version_patterns()` function has been renamed to +`load_version_declarations()` and now return a list of +`VersionDeclaration` implementations. + +Close #245 Close #275 ([`9b62a7e`](https://github.com/python-semantic-release/python-semantic-release/commit/9b62a7e377378667e716384684a47cdf392093fa)) ### Style @@ -1617,8 +1617,8 @@ Fixes #311 ([`e2d8e47`](https://github.com/python-semantic-release/python-semant * feat(github): retry GitHub API requests on failure (#314) -* refactor(github): use requests.Session to call raise_for_status - +* refactor(github): use requests.Session to call raise_for_status + * fix(github): add retries to github API requests ([`ac241ed`](https://github.com/python-semantic-release/python-semantic-release/commit/ac241edf4de39f4fc0ff561a749fa85caaf9e2ae)) ### Style @@ -1712,10 +1712,10 @@ Fixes: #277 ([`ab3061a`](https://github.com/python-semantic-release/python-seman * fix(history): coerce version to string (#298) -The changes in #297 mistakenly omitted coercing the return value to a -string. This resulted in errors like: -"can only concatenate str (not "VersionInfo") to str" - +The changes in #297 mistakenly omitted coercing the return value to a +string. This resulted in errors like: +"can only concatenate str (not "VersionInfo") to str" + Add test case asserting it's type str ([`d4cdc3d`](https://github.com/python-semantic-release/python-semantic-release/commit/d4cdc3d3cd2d93f2a78f485e3ea107ac816c7d00)) * fix(history): require semver >= 2.10 @@ -1791,8 +1791,8 @@ the end of a PR that is merged via rebase merge or merge commit. ([`93e48c9`](ht * feat(changelog): add PR links in markdown (#282) -GitHub release notes automagically link to the PR, but changelog -markdown doesn't. Replace a PR number at the end of a message +GitHub release notes automagically link to the PR, but changelog +markdown doesn't. Replace a PR number at the end of a message with a markdown link. ([`0448f6c`](https://github.com/python-semantic-release/python-semantic-release/commit/0448f6c350bbbf239a81fe13dc5f45761efa7673)) ### Style @@ -1816,7 +1816,7 @@ To control if bump major or not when current major version is zero. ([`d324154`] * refactor(history): move changelog_scope default (#284) -* Move the default for changelog_scope from inline to defaults.cfg. +* Move the default for changelog_scope from inline to defaults.cfg. * Add missing header in docs. ([`b7e1376`](https://github.com/python-semantic-release/python-semantic-release/commit/b7e1376ee1688e5e6dcc069ce623f49e3a389052)) ### Style @@ -1832,13 +1832,13 @@ A few settings were not in alphabetical order. ([`60a3535`](https://github.com/p * feat(logs): include scope in changelogs (#281) -When the scope is set, include it in changelogs, e.g. -"feat(x): some description" becomes "**x**: some description". -This is similar to how the Node semantic release (and -conventional-changelog-generator) generates changelogs. -If scope is not given, it's omitted. - -Add a new config parameter changelog_scope to disable this behavior when +When the scope is set, include it in changelogs, e.g. +"feat(x): some description" becomes "**x**: some description". +This is similar to how the Node semantic release (and +conventional-changelog-generator) generates changelogs. +If scope is not given, it's omitted. + +Add a new config parameter changelog_scope to disable this behavior when set to 'False' ([`21c96b6`](https://github.com/python-semantic-release/python-semantic-release/commit/21c96b688cc44cc6f45af962ffe6d1f759783f37)) ### Style @@ -1884,11 +1884,11 @@ Fixes #260 ([`7cacca1`](https://github.com/python-semantic-release/python-semant * ci: check commit logs with commitlint (#263) -The contributing guide says that the project should itself follow the -Angular commit convention, but there is nothing to enforce it AFAIK. - -I had a similar problem on a project where I'm using -`python-semantic-release` and I've added a Github action to +The contributing guide says that the project should itself follow the +Angular commit convention, but there is nothing to enforce it AFAIK. + +I had a similar problem on a project where I'm using +`python-semantic-release` and I've added a Github action to test it on CI, you might find it useful too. ([`016fde6`](https://github.com/python-semantic-release/python-semantic-release/commit/016fde683924d380d25579bd0cff0c7f8b7b2240)) ### Documentation @@ -1913,7 +1913,7 @@ the method above. ([`5a5e2cf`](https://github.com/python-semantic-release/python * fix: add required to inputs in action metadata (#264) -According to the documentation, `inputs.<input_id>.required` is a +According to the documentation, `inputs.<input_id>.required` is a required field. ([`e76b255`](https://github.com/python-semantic-release/python-semantic-release/commit/e76b255cf7d3d156e3314fc28c54d63fa126e973)) @@ -1970,8 +1970,8 @@ Fixes #250 ([`87e2bb8`](https://github.com/python-semantic-release/python-semant * docs: give example of multiple build commands (#248) -I had a little trouble figuring out how to use a non-setup.py build -command, so I thought it would be helpful to update the docs with an +I had a little trouble figuring out how to use a non-setup.py build +command, so I thought it would be helpful to update the docs with an example of how to do this. ([`65f1ffc`](https://github.com/python-semantic-release/python-semantic-release/commit/65f1ffcc6cac3bf382f4b821ff2be59d04f9f867)) ### Fix @@ -1989,13 +1989,13 @@ example of how to do this. ([`65f1ffc`](https://github.com/python-semantic-relea * feat: bump versions in multiple files (#246) -- Add the `version_pattern` setting, which allows version numbers to be - identified using arbitrary regular expressions. -- Refactor the config system to allow non-string data types to be - specified in `pyproject.toml`. -- Multiple files can now be specified by setting `version_variable` or - `version_pattern` to a list in `pyproject.toml`. - +- Add the `version_pattern` setting, which allows version numbers to be + identified using arbitrary regular expressions. +- Refactor the config system to allow non-string data types to be + specified in `pyproject.toml`. +- Multiple files can now be specified by setting `version_variable` or + `version_pattern` to a list in `pyproject.toml`. + Fixes #175 ([`0ba2c47`](https://github.com/python-semantic-release/python-semantic-release/commit/0ba2c473c6e44cc326b3299b6ea3ddde833bdb37)) ### Style @@ -2016,9 +2016,9 @@ Fixes #175 ([`0ba2c47`](https://github.com/python-semantic-release/python-semant * feat(changelog): add changelog_table component (#242) -Add an alternative changelog component which displays each section as a -row in a table. - +Add an alternative changelog component which displays each section as a +row in a table. + Fixes #237 ([`fe6a7e7`](https://github.com/python-semantic-release/python-semantic-release/commit/fe6a7e7fa014ffb827a1430dbcc10d1fc84c886b)) ### Style @@ -2036,25 +2036,25 @@ Fixes #237 ([`fe6a7e7`](https://github.com/python-semantic-release/python-semant * feat(changelog): add changelog components (#240) -* feat(changelog): add changelog components - -Add the ability to configure sections of the changelog using a -`changelog_components` option. Component outputs are separated by a blank -line and appear in the same order as they were configured. - -It is possible to create your own custom components. Each component is a -function which returns either some text to be added, or None in which case it -will be skipped. - -BREAKING CHANGE: The `compare_url` option has been removed in favor of using -`changelog_components`. This functionality is now available as the -`semantic_release.changelog.compare_url` component. - -* docs: add documentation for changelog_components - -* feat: pass changelog_sections to components - -Changelog components may now receive the value of `changelog_sections`, +* feat(changelog): add changelog components + +Add the ability to configure sections of the changelog using a +`changelog_components` option. Component outputs are separated by a blank +line and appear in the same order as they were configured. + +It is possible to create your own custom components. Each component is a +function which returns either some text to be added, or None in which case it +will be skipped. + +BREAKING CHANGE: The `compare_url` option has been removed in favor of using +`changelog_components`. This functionality is now available as the +`semantic_release.changelog.compare_url` component. + +* docs: add documentation for changelog_components + +* feat: pass changelog_sections to components + +Changelog components may now receive the value of `changelog_sections`, split and ready to use. ([`3e17a98`](https://github.com/python-semantic-release/python-semantic-release/commit/3e17a98d7fa8468868a87e62651ac2c010067711)) ### Style @@ -2081,30 +2081,30 @@ Fixes #239 ([`34acbbc`](https://github.com/python-semantic-release/python-semant * feat(history): create emoji parser (#238) -Add a commit parser which uses emojis from https://gitmoji.carloscuesta.me/ -to determine the type of change. - -* fix: add emojis to default changelog_sections - -* fix: include all parsed types in changelog - -This allows emojis to appear in the changelog, as well as configuring -other types to appear with the Angular parser (I remember someone asking -for that feature a while ago). All filtering is now done in the -markdown_changelog function. - -* refactor(history): get breaking changes in parser - -Move the task of detecting breaking change descriptions into the commit -parser function, instead of during changelog generation. - -This has allowed the emoji parser to also return the regular descriptions as -breaking change descriptions for commits with :boom:. - -BREAKING CHANGE: Custom commit parser functions are now required to pass -a fifth argument to `ParsedCommit`, which is a list of breaking change -descriptions. - +Add a commit parser which uses emojis from https://gitmoji.carloscuesta.me/ +to determine the type of change. + +* fix: add emojis to default changelog_sections + +* fix: include all parsed types in changelog + +This allows emojis to appear in the changelog, as well as configuring +other types to appear with the Angular parser (I remember someone asking +for that feature a while ago). All filtering is now done in the +markdown_changelog function. + +* refactor(history): get breaking changes in parser + +Move the task of detecting breaking change descriptions into the commit +parser function, instead of during changelog generation. + +This has allowed the emoji parser to also return the regular descriptions as +breaking change descriptions for commits with :boom:. + +BREAKING CHANGE: Custom commit parser functions are now required to pass +a fifth argument to `ParsedCommit`, which is a list of breaking change +descriptions. + * docs: add documentation for emoji parser ([`2e1c50a`](https://github.com/python-semantic-release/python-semantic-release/commit/2e1c50a865628b372f48945a039a3edb38a7cdf0)) ### Style @@ -2178,12 +2178,12 @@ Any issues which are labelled as a question will be closed after two weeks of in * ci: pass SHA from beautify to release -Checkout the current SHA from the end of the beautify job for releasing, -instead of master. This will either be the same as the commit we are -running for, or the SHA of a style commit. This prevents releasing of +Checkout the current SHA from the end of the beautify job for releasing, +instead of master. This will either be the same as the commit we are +running for, or the SHA of a style commit. This prevents releasing of untested code. -See +See https://github.community/t5/GitHub-Actions/Checkout-commit-pushed-by-previous-job/m-p/55847#M9670 ([`76e34b6`](https://github.com/python-semantic-release/python-semantic-release/commit/76e34b6b52b8019e87eaddf295d0781b6aa51541)) ### Documentation @@ -2261,15 +2261,15 @@ This was missed in 213530fb0c914e274b81d1dacf38ea7322b5b91f ([`3084249`](https:/ * refactor(debug): use logging and click_log instead of ndebug -BREAKING CHANGE: `DEBUG="*"` no longer has an effect, instead use +BREAKING CHANGE: `DEBUG="*"` no longer has an effect, instead use `--verbosity DEBUG`. ([`15b1f65`](https://github.com/python-semantic-release/python-semantic-release/commit/15b1f650f29761e1ab2a91b767cbff79b2057a4c)) ### Build * build(pip): store requirements in setup.py -Remove the requirements directory and instead store all required -libraries directly inside setup.py. Development, testing and docs +Remove the requirements directory and instead store all required +libraries directly inside setup.py. Development, testing and docs dependencies are included as extras. ([`401468f`](https://github.com/python-semantic-release/python-semantic-release/commit/401468f312cf4f3b52006c68c58c4645b5e19802)) ### Chore @@ -2282,12 +2282,12 @@ Allow mypy and coverage to run on any Python version. ([`28feba6`](https://githu * ci: always checkout most recent commit to release -This should pull a beautify commit if one has been created, allowing the +This should pull a beautify commit if one has been created, allowing the new version to be pushed. ([`6c98aab`](https://github.com/python-semantic-release/python-semantic-release/commit/6c98aab932724e3aab08e68b75439bc8c31bd877)) * ci: cache testing dependencies -This should help improve the speed of the testing workflow by caching +This should help improve the speed of the testing workflow by caching downloaded dependencies. ([`4f53e35`](https://github.com/python-semantic-release/python-semantic-release/commit/4f53e351960a6b658f50265384c9e8f678718f68)) * ci: move beautification to separate workflow @@ -2304,7 +2304,7 @@ Run isort and Black on pushes to master. Any edits made are committed. isort and * docs: include README.rst in index.rst -These files were very similar so it makes sense to simply include one +These files were very similar so it makes sense to simply include one inside the other. ([`8673a9d`](https://github.com/python-semantic-release/python-semantic-release/commit/8673a9d92a9bf348bb3409e002a830741396c8ca)) * docs: rewrite README.rst ([`e049772`](https://github.com/python-semantic-release/python-semantic-release/commit/e049772cf14cdd49538cf357db467f0bf3fe9587)) @@ -2334,12 +2334,12 @@ inside the other. ([`8673a9d`](https://github.com/python-semantic-release/python * ci: fetch full history in release job -I didn't realise that actions/checkout@v2 only fetches 1 commit by +I didn't realise that actions/checkout@v2 only fetches 1 commit by default. ([`a02a9b7`](https://github.com/python-semantic-release/python-semantic-release/commit/a02a9b7e34d8e7f8bb3b9c8aa1b5e1ef8bdd406c)) * ci: run tests on pull_request -The tests didn't run for #211 which caused a flake8 failure to be +The tests didn't run for #211 which caused a flake8 failure to be missed. ([`32fd77e`](https://github.com/python-semantic-release/python-semantic-release/commit/32fd77ed835bcfc943abeacec4e327df045b2ec9)) * ci: run tests on GitHub Actions ([`39ff283`](https://github.com/python-semantic-release/python-semantic-release/commit/39ff283312a0c686bfc5be71e1da9b6456652d95)) @@ -2362,7 +2362,7 @@ Automatically create pages in the API docs section using sphinx-autodoc. This is * style: fix styling from 2997908 -These code style problems were introduced because tests didn't run on +These code style problems were introduced because tests didn't run on #211. ([`172391e`](https://github.com/python-semantic-release/python-semantic-release/commit/172391ec5b5e490081b9b0ea58a94dfd5be33937)) @@ -2391,31 +2391,31 @@ These code style problems were introduced because tests didn't run on * fix: Bump dependencies and fix Windows issues on Development (#173) -* Bump dependencies and fix windows issues - -* Correctly pass temp dir to test settings - -* Remove print call on test settings - -* chore: remove py34 and py35 classifiers - -* chore: bump twine, requests and python-gitlab - -* chore: update tox config to be more granular - -* fix: missing mime types on Windows - -* chore: bump circleCI and tox python to 3.8 - -* chore: remove py36 from tox envlist - +* Bump dependencies and fix windows issues + +* Correctly pass temp dir to test settings + +* Remove print call on test settings + +* chore: remove py34 and py35 classifiers + +* chore: bump twine, requests and python-gitlab + +* chore: update tox config to be more granular + +* fix: missing mime types on Windows + +* chore: bump circleCI and tox python to 3.8 + +* chore: remove py36 from tox envlist + * chore: isort errors ([`0a6f8c3`](https://github.com/python-semantic-release/python-semantic-release/commit/0a6f8c3842b05f5f424dad5ce1fa5e3823c7e688)) ### Refactor * refactor(history): use a named tuple for parsed commits -This improves readability as we can use attributes such as 'bump' and +This improves readability as we can use attributes such as 'bump' and 'descriptions' instead of confusing numeric indices. ([`bff40d5`](https://github.com/python-semantic-release/python-semantic-release/commit/bff40d53174ffe27451d82132c31b112c7bee9fd)) @@ -2454,10 +2454,10 @@ will get the version they specify. ([`123984d`](https://github.com/python-semant * feat(build): allow config setting for build command (#195) -* feat(build): allow config setting for build command - -BREAKING CHANGE: Previously the build_commands configuration variable set the types of bundles sent to `python setup.py`. It has been replaced by the configuration variable `build_command` which takes the full command e.g. `python setup.py sdist` or `poetry build`. - +* feat(build): allow config setting for build command + +BREAKING CHANGE: Previously the build_commands configuration variable set the types of bundles sent to `python setup.py`. It has been replaced by the configuration variable `build_command` which takes the full command e.g. `python setup.py sdist` or `poetry build`. + Closes #188 ([`740f4bd`](https://github.com/python-semantic-release/python-semantic-release/commit/740f4bdb26569362acfc80f7e862fc2c750a46dd)) ### Fix @@ -2555,46 +2555,46 @@ Fixes #181 ([`0fddbe2`](https://github.com/python-semantic-release/python-semant * feat: Upload distribution files to GitHub Releases (#177) -* refactor(github): create upload_asset function - -Create a function to call the asset upload API. This will soon be used -to upload assets specified by the user. - -* refactor(github): infer Content-Type from file extension - -Infer the Content-Type header based on the file extension instead of setting it manually. - -* refactor(pypi): move building of dists to cli.py - -Refactor to have the building/removal of distributions in cli.py instead -of within the upload_to_pypi function. This makes way for uploading to -other locations, such as GitHub Releases, too. - -* feat(github): upload dists to release - -Upload Python wheels to the GitHub release. Configured with the option -upload_to_release, on by default if using GitHub. - -* docs: document upload_to_release config option - -* test(github): add tests for Github.upload_dists - -* fix(github): fix upload of .whl files - -Fix uploading of .whl files due to a missing MIME type (defined custom type as application/x-wheel+zip). Additionally, continue with other uploads even if one fails. - -* refactor(cli): additional output during publish - -Add some additional output during the publish command. - -* refactor(github): move api calls to separate methods - -Move each type of GitHub API request into its own method to improve readability. - -Re-implementation of #172 - -* fix: post changelog after PyPI upload - +* refactor(github): create upload_asset function + +Create a function to call the asset upload API. This will soon be used +to upload assets specified by the user. + +* refactor(github): infer Content-Type from file extension + +Infer the Content-Type header based on the file extension instead of setting it manually. + +* refactor(pypi): move building of dists to cli.py + +Refactor to have the building/removal of distributions in cli.py instead +of within the upload_to_pypi function. This makes way for uploading to +other locations, such as GitHub Releases, too. + +* feat(github): upload dists to release + +Upload Python wheels to the GitHub release. Configured with the option +upload_to_release, on by default if using GitHub. + +* docs: document upload_to_release config option + +* test(github): add tests for Github.upload_dists + +* fix(github): fix upload of .whl files + +Fix uploading of .whl files due to a missing MIME type (defined custom type as application/x-wheel+zip). Additionally, continue with other uploads even if one fails. + +* refactor(cli): additional output during publish + +Add some additional output during the publish command. + +* refactor(github): move api calls to separate methods + +Move each type of GitHub API request into its own method to improve readability. + +Re-implementation of #172 + +* fix: post changelog after PyPI upload + Post the changelog in-between uploading to PyPI and uploading to GitHub Releases. This is so that if the PyPI upload fails, GitHub users will not be notified. GitHub uploads still need to be processed after creating the changelog as the release notes must be published to upload assets to them. ([`e427658`](https://github.com/python-semantic-release/python-semantic-release/commit/e427658e33abf518191498c3142a0f18d3150e07)) ### Fix @@ -2614,7 +2614,7 @@ application/octet-stream is more generic, but it is better than using a non-offi * feat(history): capitalize changelog messages -Capitalize the first letter of messages in the changelog regardless of +Capitalize the first letter of messages in the changelog regardless of whether they are capitalized in the commit itself. ([`1a8e306`](https://github.com/python-semantic-release/python-semantic-release/commit/1a8e3060b8f6d6362c27903dcfc69d17db5f1d36)) ### Fix @@ -2652,7 +2652,7 @@ Fix the syntax of a broken bullet-point list in README.rst. ([`7aa572b`](https:/ * fix(github): send token in request header -Use an Authorization header instead of deprecated query parameter +Use an Authorization header instead of deprecated query parameter authorization. Fixes relekang/python-semantic-release#167 ([`be9972a`](https://github.com/python-semantic-release/python-semantic-release/commit/be9972a7b1fb183f738fb31bd370adb30281e4d5)) @@ -2723,13 +2723,13 @@ Fixes #156 ([`a4f8a10`](https://github.com/python-semantic-release/python-semant * fix: fallback to whole log if correct tag is not available (#157) -The method getting all commits to consider for the release will now test -whether the version in input is a valid reference. If it is not, it will -consider the whole log for the repository. - -evaluate_version_bump will still consider a message starting with the -version number as a breaking condition to stop analyzing. - +The method getting all commits to consider for the release will now test +whether the version in input is a valid reference. If it is not, it will +consider the whole log for the repository. + +evaluate_version_bump will still consider a message starting with the +version number as a breaking condition to stop analyzing. + Fixes #51 ([`252bffd`](https://github.com/python-semantic-release/python-semantic-release/commit/252bffd3be7b6dfcfdb384d24cb1cd83d990fc9a)) @@ -2739,14 +2739,14 @@ Fixes #51 ([`252bffd`](https://github.com/python-semantic-release/python-semanti * fix: Set version of click to >=2.0,<8.0. (#155) -* fix: Upgrade to click 7.0. - -Fixes #117 - -* fix: Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. - -* Upstream is at ~=7.0, so let's set the range to less than 8.0. - +* fix: Upgrade to click 7.0. + +Fixes #117 + +* fix: Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. + +* Upstream is at ~=7.0, so let's set the range to less than 8.0. + * The string template has no variables, so remove the call to .format() ([`f07c7f6`](https://github.com/python-semantic-release/python-semantic-release/commit/f07c7f653be1c018e443f071d9a196d9293e9521)) @@ -2937,9 +2937,9 @@ The commands is lacking from the documentation. ([`b6fa04d`](https://github.com/ ### Refactor -* refactor: added debug to hvcshvcs +* refactor: added debug to hvcshvcs + - module did not have any debug ([`0c6237b`](https://github.com/python-semantic-release/python-semantic-release/commit/0c6237bc01ec39608fb768925091c755d9bb25bd)) * refactor: fix import sorting ([`01e4c5d`](https://github.com/python-semantic-release/python-semantic-release/commit/01e4c5d743f2f237d2c85481118e467d4f5fde15)) @@ -2963,14 +2963,14 @@ module did not have any debug ([`0c6237b`](https://github.com/python-semantic-re * fix: Maintain version variable formatting on bump (#103) -Small change to the way the version is written to the config file it is read from. This allows the formatting to be the same as before semantic-release changed it. - -Prior behavior -`my_version_var="1.2.3"` => `my_version_var = '1.2.4'` - -New behavior -`my_version_var="1.2.3"` => `my_version_var="1.2.4"` - +Small change to the way the version is written to the config file it is read from. This allows the formatting to be the same as before semantic-release changed it. + +Prior behavior +`my_version_var="1.2.3"` => `my_version_var = '1.2.4'` + +New behavior +`my_version_var="1.2.3"` => `my_version_var="1.2.4"` + I am using python-semantic-release with a Julia project and this change will allow for consistent formatting in my Project.toml file where the version is maintained ([`bf63156`](https://github.com/python-semantic-release/python-semantic-release/commit/bf63156f60340614fae94c255fb2f097cf317b2b)) * fix: initialize git Repo from current folder @@ -3099,8 +3099,8 @@ Closes #88 re #32 ([`8df5e2b`](https://github.com/python-semantic-release/pytho * fix: Change requests from fixed version to version range (#93) -* Change requests version to be more flexible to aid in using this with dev requirements for a release. - +* Change requests version to be more flexible to aid in using this with dev requirements for a release. + * revert changes to vcs helpers ([`af3ad59`](https://github.com/python-semantic-release/python-semantic-release/commit/af3ad59f018876e11cc3acdda0b149f8dd5606bd)) ### Refactor @@ -3172,21 +3172,21 @@ Change the Gitpython version number to fix a bug described in #80. ([`23c9d4b`]( * feat: Add support to finding previous version from tags if not using commit messages (#68) -* feat: Be a bit more forgiving to find previous tags - -Now grabs the previous version from tag names if it can't find it in the commit - -* quantifiedcode and flake8 fixes - -* Update cli.py - +* feat: Be a bit more forgiving to find previous tags + +Now grabs the previous version from tag names if it can't find it in the commit + +* quantifiedcode and flake8 fixes + +* Update cli.py + * Switch to ImproperConfigurationError ([`6786487`](https://github.com/python-semantic-release/python-semantic-release/commit/6786487ebf4ab481139ef9f43cd74e345debb334)) * feat: Add --retry cli option (#78) -* Add --retry cli option -* Post changelog correctly -* Add comments +* Add --retry cli option +* Post changelog correctly +* Add comments * Add --retry to the docs ([`3e312c0`](https://github.com/python-semantic-release/python-semantic-release/commit/3e312c0ce79a78d25016a3b294b772983cfb5e0f)) ### Fix @@ -3222,9 +3222,9 @@ Fixes #74 ([`1dc306b`](https://github.com/python-semantic-release/python-semanti * fix: error when not in git repository (#75) -Fix an error when the program was run in a non-git repository. It would -not allow the help options to be run. - +Fix an error when the program was run in a non-git repository. It would +not allow the help options to be run. + issue #74 ([`251b190`](https://github.com/python-semantic-release/python-semantic-release/commit/251b190a2fd5df68892346926d447cbc1b32475a)) ### Unknown @@ -3278,12 +3278,12 @@ This reverts commit 93e5507da6d53ecf63405507390633ef480c52fb. ([`195ed8d`](https * feat: Add git hash to the changelog (#65) -* feat(*): add git hash to the changelog - -Add git hash to the changelog to ease finding the specific commit. The hash now is also easily viewable in Github's tag. see #63 for more information. - -* chore(test_history): fix test errors - +* feat(*): add git hash to the changelog + +Add git hash to the changelog to ease finding the specific commit. The hash now is also easily viewable in Github's tag. see #63 for more information. + +* chore(test_history): fix test errors + Fix the test errors that would happen after the modification of get_commit_log. ([`628170e`](https://github.com/python-semantic-release/python-semantic-release/commit/628170ebc440fc6abf094dd3e393f40576dedf9b)) ### Fix @@ -3997,7 +3997,7 @@ parsed. ([`974ccda`](https://github.com/python-semantic-release/python-semantic- * Merge pull request #15 from jezdez/python-2 -Add Python 2.7 support. Fix #10. +Add Python 2.7 support. Fix #10. :sparkles: ([`5daabb7`](https://github.com/python-semantic-release/python-semantic-release/commit/5daabb75eb9145566a2a7c2a9e64439df7cd85f1)) * Add Python 2.7 support. Fix #10. ([`c05e13f`](https://github.com/python-semantic-release/python-semantic-release/commit/c05e13f22163237e963c493ffeda7e140f0202c6)) diff --git a/README.rst b/README.rst index 73f30b366..70167f553 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Python Semantic Release *********************** -|Black| |Ruff| |Test Status| |PyPI Version| |conda-forge version| |Read the Docs Status| |Pre-Commit Enabled| +|Ruff| |Test Status| |PyPI Version| |conda-forge version| |Read the Docs Status| |Pre-Commit Enabled| Automatic Semantic Versioning for Python projects. This is a Python implementation of `semantic-release`_ for JS by Stephan Bönnemann. If @@ -30,9 +30,6 @@ Read more about the setup and configuration in our `getting started guide`_. .. _GitHub Action: https://python-semantic-release.readthedocs.io/en/latest/automatic-releases/github-actions.html .. _conda-forge: https://anaconda.org/conda-forge/python-semantic-release -.. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - :alt: black .. |Test Status| image:: https://img.shields.io/github/actions/workflow/status/python-semantic-release/python-semantic-release/main.yml?branch=master&label=Test%20Status&logo=github :target: https://github.com/python-semantic-release/python-semantic-release/actions/workflows/main.yml :alt: test-status diff --git a/pyproject.toml b/pyproject.toml index 8089115b6..cbbac2b48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,7 @@ test = [ "requests-mock>=1.10.0,<2", "types-pytest-lazy-fixture>=0.6.3.3", ] -dev = ["pre-commit", "tox", "black", "ruff==0.1.1"] +dev = ["pre-commit", "tox", "ruff==0.1.2"] mypy = ["mypy", "types-requests"] [tool.pytest.ini_options] @@ -163,23 +163,21 @@ allow_untyped_calls = true select = ["ALL"] # See https://beta.ruff.rs/docs/rules +# for any of these codes you can also run `ruff rule [CODE]` +# which explains it in the terminal ignore = [ # attribute shadows builtin (e.g. Foo.list()) "A003", # Annotations (flake8-annotations) # missing "self" type-hint "ANN101", - # missing "cls" type-hint "ANN102", - # no typing.Any "ANN401", # flake8-bugbear - # no functools.lru_cache "B019", # flake8-commas "COM", - # Missing docstrings. These would be nice to enable - # but not done yet + # Missing docstrings - eventually want to enable "D100", "D101", "D102", @@ -188,17 +186,11 @@ ignore = [ "D105", "D107", - # class docstrings must be preceded by a newline "D203", - # one blank line between summary and description for docstrings "D205", - # multiline summary should start on first line of docstring "D212", - # first line should end with a period "D400", - # "an imperative mood" "D401", - # First word of docstring should be "This" "D404", "D415", # flake8-datetimez @@ -221,9 +213,7 @@ ignore = [ "PT007", # pytest.raises needs a match - eventually want to enable "PT011", - # pytest.raises must be alone in a with-block "PT012", - # use only simple "import pytest" "PT013", # pylint "PLR", @@ -243,6 +233,24 @@ ignore = [ # tryceratops "TRY003", "TRY401", + + # other errors that conflict with ruff format + # indentation-with-invalid-multiple + "W191", + "E111", + "E114", + "E117", + "E501", + "D206", + "D300", + "Q000", + "Q001", + "Q002", + "Q003", + "COM812", + "COM812", + "ISC001", + "ISC002", ] external = ["V"] @@ -256,6 +264,11 @@ show-fixes = true src = ["semantic_release", "tests"] task-tags = ["NOTE", "TODO", "FIXME", "XXX"] +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +line-ending = "lf" + [tool.ruff.per-file-ignores] # Imported but unused "__init__.py" = ["F401"] @@ -296,6 +309,10 @@ max-complexity = 10 [tool.ruff.flake8-implicit-str-concat] allow-multiline = true +[tool.ruff.flake8-quotes] +inline-quotes = "double" +multiline-quotes = "double" + [tool.ruff.flake8-tidy-imports] ban-relative-imports = "all" diff --git a/semantic_release/hvcs/gitea.py b/semantic_release/hvcs/gitea.py index 1d80a90d7..c4196ae51 100644 --- a/semantic_release/hvcs/gitea.py +++ b/semantic_release/hvcs/gitea.py @@ -154,7 +154,10 @@ def asset_upload_url(self, release_id: str) -> str: @logged_function(log) def upload_asset( - self, release_id: int, file: str, label: str | None = None # noqa: ARG002 + self, + release_id: int, + file: str, + label: str | None = None, # noqa: ARG002 ) -> bool: """ Upload an asset to an existing release diff --git a/semantic_release/hvcs/gitlab.py b/semantic_release/hvcs/gitlab.py index 378901780..1d684a502 100644 --- a/semantic_release/hvcs/gitlab.py +++ b/semantic_release/hvcs/gitlab.py @@ -81,7 +81,10 @@ def _get_repository_owner_and_name(self) -> tuple[str, str]: @logged_function(log) def create_release( - self, tag: str, release_notes: str, prerelease: bool = False # noqa: ARG002 + self, + tag: str, + release_notes: str, + prerelease: bool = False, # noqa: ARG002 ) -> str: """ Post release changelog diff --git a/semantic_release/version/translator.py b/semantic_release/version/translator.py index 24b295cfc..7a4ce275f 100644 --- a/semantic_release/version/translator.py +++ b/semantic_release/version/translator.py @@ -41,7 +41,9 @@ def _invert_tag_format_to_re(cls, tag_format: str) -> re.Pattern[str]: return pat def __init__( - self, tag_format: str = "v{version}", prerelease_token: str = "rc" # noqa: S107 + self, + tag_format: str = "v{version}", + prerelease_token: str = "rc", # noqa: S107 ) -> None: check_tag_format(tag_format) self.tag_format = tag_format diff --git a/tests/unit/semantic_release/hvcs/test_gitea.py b/tests/unit/semantic_release/hvcs/test_gitea.py index 13441b4c5..ef293ff36 100644 --- a/tests/unit/semantic_release/hvcs/test_gitea.py +++ b/tests/unit/semantic_release/hvcs/test_gitea.py @@ -606,9 +606,7 @@ def test_upload_dists_when_release_id_found( default_gitea_client, "upload_asset" ) as mock_upload_asset, mock.patch.object( glob, "glob" - ) as mock_glob_glob, mock.patch.object( - os.path, "isfile" - ) as mock_os_path_isfile: + ) as mock_glob_glob, mock.patch.object(os.path, "isfile") as mock_os_path_isfile: # Skip check as the files don't exist in filesystem mock_os_path_isfile.return_value = True diff --git a/tests/unit/semantic_release/hvcs/test_github.py b/tests/unit/semantic_release/hvcs/test_github.py index 97a7759f5..a17697fa2 100644 --- a/tests/unit/semantic_release/hvcs/test_github.py +++ b/tests/unit/semantic_release/hvcs/test_github.py @@ -538,13 +538,14 @@ def test_asset_upload_url(default_gh_client): } with requests_mock.Mocker(session=default_gh_client.session) as m: m.register_uri("GET", github_api_matcher, json=resp_payload, status_code=200) - assert default_gh_client.asset_upload_url( - release_id - ) == "https://{domain}/repos/{owner}/{repo}/releases/{release_id}/assets".format( - domain=default_gh_client.DEFAULT_UPLOAD_DOMAIN, - owner=default_gh_client.owner, - repo=default_gh_client.repo_name, - release_id=release_id, + assert ( + default_gh_client.asset_upload_url(release_id) + == "https://{domain}/repos/{owner}/{repo}/releases/{release_id}/assets".format( + domain=default_gh_client.DEFAULT_UPLOAD_DOMAIN, + owner=default_gh_client.owner, + repo=default_gh_client.repo_name, + release_id=release_id, + ) ) assert m.called assert len(m.request_history) == 1 @@ -704,9 +705,7 @@ def test_upload_dists_when_release_id_found( default_gh_client, "upload_asset" ) as mock_upload_asset, mock.patch.object( glob, "glob" - ) as mock_glob_glob, mock.patch.object( - os.path, "isfile" - ) as mock_os_path_isfile: + ) as mock_glob_glob, mock.patch.object(os.path, "isfile") as mock_os_path_isfile: # Skip check as the filenames deliberately don't exists for testing mock_os_path_isfile.return_value = True From a176d626f28ba68ae8a938b2f04f74da841a7eeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:20:07 +0000 Subject: [PATCH 002/167] build(deps-dev): update python-gitlab requirement from <4,>=2 to >=2,<5 (#748) Updates the requirements on [python-gitlab](https://github.com/python-gitlab/python-gitlab) to permit the latest version. - [Release notes](https://github.com/python-gitlab/python-gitlab/releases) - [Changelog](https://github.com/python-gitlab/python-gitlab/blob/main/CHANGELOG.md) - [Commits](https://github.com/python-gitlab/python-gitlab/compare/v2.0.0...v4.1.1) --- updated-dependencies: - dependency-name: python-gitlab dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index cbbac2b48..3ee423f8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ "gitpython>=3.0.8,<4", "requests>=2.25,<3", "jinja2>=3.1.2,<4", - "python-gitlab>=2,<4", + "python-gitlab>=2,<5", "tomlkit~=0.10", "dotty-dict>=1.3.0,<2", "importlib-resources>=5.7,<7", From 90db8f1bd8986eda1b913cd4bab5abd41192f01f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:23:07 +0000 Subject: [PATCH 003/167] build(deps-dev): bump ruff from 0.1.2 to 0.1.6 (#757) Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.2 to 0.1.6. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/v0.1.2...v0.1.6) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3ee423f8e..6d97ddb21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,7 @@ test = [ "requests-mock>=1.10.0,<2", "types-pytest-lazy-fixture>=0.6.3.3", ] -dev = ["pre-commit", "tox", "ruff==0.1.2"] +dev = ["pre-commit", "tox", "ruff==0.1.6"] mypy = ["mypy", "types-requests"] [tool.pytest.ini_options] From 90380d797a734dcca5040afc5fa00e3e01f64152 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 7 Dec 2023 11:24:14 -0500 Subject: [PATCH 004/167] docs(migration): fix comments about publish command (#747) --- docs/migrating_from_v7.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/migrating_from_v7.rst b/docs/migrating_from_v7.rst index d99c2c004..8db5afc35 100644 --- a/docs/migrating_from_v7.rst +++ b/docs/migrating_from_v7.rst @@ -176,7 +176,8 @@ To achieve a similar flow of logic such as 4. Push the changes to the metadata and changelog to the remote repository 5. Create a release in the remote version control system 6. Build a wheel - 7. Publish the wheel to PyPI and to the release in the remote VCS + 7. Publish the wheel to PyPI + 8. Publish the distribution artifacts to the release in the remote VCS You should run:: @@ -184,8 +185,9 @@ You should run:: twine upload dist/* # or whichever path your distributions are placed in semantic-release publish -With steps 1-5 being handled by the :ref:`cmd-version` command, and steps 6 and 7 -handled by the :ref:`cmd-publish` command. +With steps 1-6 being handled by the :ref:`cmd-version` command, step 7 being left +to the developer to handle, and lastly step 8 to be handled by the +:ref:`cmd-publish` command. .. _breaking-removed-define-option: From 744ff256f10c895a97241dc085bc132d10c9f737 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 7 Dec 2023 11:25:14 -0500 Subject: [PATCH 005/167] test(commandline-main): prevent git gpgsign config from failing tests (#760) --- tests/command_line/test_main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/command_line/test_main.py b/tests/command_line/test_main.py index 34b618113..dc7a97774 100644 --- a/tests/command_line/test_main.py +++ b/tests/command_line/test_main.py @@ -111,6 +111,7 @@ def test_uses_default_config_when_no_config_file_found( with repo.config_writer("repository") as config: config.set_value("user", "name", "semantic release testing") config.set_value("user", "email", "not_a_real@email.com") + config.set_value("commit", "gpgsign", False) repo.create_remote(name="origin", url="foo@barvcs.com:user/repo.git") repo.git.commit("-m", "feat: initial commit", "--allow-empty") From de6b9ad921e697b5ea2bb2ea8f180893cecca920 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 7 Dec 2023 11:42:51 -0500 Subject: [PATCH 006/167] feat(cmd-version): add `--tag/--no-tag` option to version command (#752) * fix(version): separate push tags from commit push when not committing changes * feat(version): add `--no-tag` option to turn off tag creation * test(version): add test for `--tag` option & `--no-tag/commit` * docs(commands): update `version` subcommand options --- docs/commands.rst | 21 ++++- semantic_release/cli/commands/version.py | 56 ++++++++---- tests/command_line/test_version.py | 107 +++++++++++++++++++++++ 3 files changed, 164 insertions(+), 20 deletions(-) diff --git a/docs/commands.rst b/docs/commands.rst index 113600acf..60de6f270 100644 --- a/docs/commands.rst +++ b/docs/commands.rst @@ -213,13 +213,26 @@ For example, assuming a project is currently at version 1.2.3:: Whether or not to perform a ``git commit`` on modifications to source files made by ``semantic-release`` during this command invocation, and to run ``git tag`` on this new commit with a tag corresponding to the new version. -If ``--no-commit`` is supplied, a number of other options are also disabled; please see below. +If ``--no-commit`` is supplied, it may disable other options derivatively; please see below. **Default:** ``--commit`` .. seealso:: - :ref:`tag_format ` +.. _cmd-version-option-tag: + +``--tag/--no-tag`` +************************ + +Whether or not to perform a ``git tag`` to apply a tag of the corresponding to the new version during this +command invocation. This option manages the tag application separate from the commit handled by the `--commit` +option. + +If ``--no-tag`` is supplied, it may disable other options derivatively; please see below. + +**Default:** ``--tag`` + .. _cmd-version-option-changelog: ``--changelog/--no-changelog`` @@ -239,10 +252,10 @@ version released. ``--push/--no-push`` ******************** -Whether or not to push new commits and tags to the remote repository. +Whether or not to push new commits and/or tags to the remote repository. -**Default:** ``--no-push`` if :ref:`--no-commit ` is also -supplied, otherwise ``--push`` +**Default:** ``--no-push`` if :ref:`--no-commit ` and +:ref:`--no-tag ` is also supplied, otherwise ``push`` is the default. .. _cmd-version-option-vcs-release: diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index f9bca7acc..f1793cf2c 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -149,6 +149,12 @@ def shell(cmd: str, *, check: bool = True) -> subprocess.CompletedProcess: default=True, help="Whether or not to commit changes locally", ) +@click.option( + "--tag/--no-tag", + "create_tag", + default=True, + help="Whether or not to create a tag for the new version" +) @click.option( "--changelog/--no-changelog", "update_changelog", @@ -188,6 +194,7 @@ def version( # noqa: C901 prerelease_token: str | None = None, force_level: str | None = None, commit_changes: bool = True, + create_tag: bool = True, update_changelog: bool = True, push_changes: bool = True, make_vcs_release: bool = True, @@ -237,8 +244,14 @@ def version( # noqa: C901 # Only push if we're committing changes if push_changes and not commit_changes: - log.info("changes will not be pushed because --no-commit disables pushing") - push_changes &= commit_changes + if not create_tag: + log.info("changes will not be pushed because --no-commit disables pushing") + push_changes &= commit_changes + # Only push if we're creating a tag + if push_changes and not create_tag: + if not commit_changes: + log.info("new tag will not be pushed because --no-tag disables pushing") + push_changes &= create_tag # Only make a release if we're pushing the changes if make_vcs_release and not push_changes: log.info("No vcs release will be created because pushing changes is disabled") @@ -484,36 +497,47 @@ def custom_git_environment() -> ContextManager[None]: # are disabled, and the changelog generation is disabled or it's not # modified, then the HEAD commit will be tagged as a release commit # despite not being made by PSR - if commit_changes and opts.noop: - noop_report( - indented( - f""" - would have run: - git tag -a {new_version.as_tag()} -m "{new_version.as_tag()}" - """ + if commit_changes or create_tag: + if opts.noop: + noop_report( + indented( + f""" + would have run: + git tag -a {new_version.as_tag()} -m "{new_version.as_tag()}" + """ + ) ) - ) - elif commit_changes: - with custom_git_environment(): - repo.git.tag("-a", new_version.as_tag(), m=new_version.as_tag()) + else: + with custom_git_environment(): + repo.git.tag("-a", new_version.as_tag(), m=new_version.as_tag()) if push_changes: remote_url = runtime.hvcs_client.remote_url( use_token=not runtime.ignore_token_for_push ) active_branch = repo.active_branch.name - if opts.noop: + if commit_changes and opts.noop: noop_report( indented( f""" would have run: git push {runtime.masker.mask(remote_url)} {active_branch} - git push --tags {runtime.masker.mask(remote_url)} {active_branch} """ # noqa: E501 ) ) - else: + elif commit_changes: repo.git.push(remote_url, active_branch) + + if create_tag and opts.noop: + noop_report( + indented( + f""" + would have run: + git push --tags {runtime.masker.mask(remote_url)} {active_branch} + """ # noqa: E501 + ) + ) + elif create_tag: repo.git.push("--tags", remote_url, active_branch) gha_output.released = True diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 5856b7129..627f38678 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -4,6 +4,7 @@ import filecmp import re import shutil +from pathlib import Path from subprocess import CompletedProcess from typing import TYPE_CHECKING from unittest import mock @@ -590,3 +591,109 @@ def test_custom_release_notes_template( ) assert post_mocker.call_count == 1 assert post_mocker.last_request.json()["body"] == expected_release_notes + + +def test_version_tag_only_push( + mocked_git_push: MagicMock, + runtime_context_with_no_tags: RuntimeContext, + cli_runner: CliRunner, +) -> None: + # Arrange + # (see fixtures) + head_before = runtime_context_with_no_tags.repo.head.commit + + # Act + args = [version.name, "--tag", "--no-commit", "--skip-build", "--no-vcs-release"] + resp = cli_runner.invoke(main, args) + + tag_after = runtime_context_with_no_tags.repo.tags[-1].name + head_after = runtime_context_with_no_tags.repo.head.commit + + # Assert + assert tag_after == "v0.1.0" + assert head_before == head_after + assert mocked_git_push.call_count == 1 # 0 for commit, 1 for tag + assert resp.exit_code == 0, ( + "Unexpected failure in command " + f"'semantic-release {str.join(' ', args)}': " + resp.stderr + ) + + +def test_version_only_update_files_no_git_actions( + mocked_git_push: MagicMock, + runtime_context_with_tags: RuntimeContext, + cli_runner: CliRunner, + tmp_path_factory: pytest.TempPathFactory, + example_pyproject_toml: Path +) -> None: + # Arrange + expected_new_version = "0.3.0" + tempdir = tmp_path_factory.mktemp("test_version") + shutil.rmtree(str(tempdir.resolve())) + example_project = Path(runtime_context_with_tags.repo.git.rev_parse("--show-toplevel")) + shutil.copytree(src=str(example_project.resolve()), dst=tempdir) + + head_before = runtime_context_with_tags.repo.head.commit + tags_before = runtime_context_with_tags.repo.tags + + # Act + args = [version.name, "--minor", "--no-tag", "--no-commit", "--skip-build"] + resp = cli_runner.invoke(main, args) + + tags_after = runtime_context_with_tags.repo.tags + head_after = runtime_context_with_tags.repo.head.commit + + # Assert + assert tags_before == tags_after + assert head_before == head_after + assert mocked_git_push.call_count == 0 # no push as it should be turned off automatically + assert resp.exit_code == 0, ( + "Unexpected failure in command " + f"'semantic-release {str.join(' ', args)}': " + resp.stderr + ) + + dcmp = filecmp.dircmp(str(example_project.resolve()), tempdir) + differing_files = flatten_dircmp(dcmp) + + # Files that should receive version change + assert differing_files == [ + "pyproject.toml", + f"src/{EXAMPLE_PROJECT_NAME}/__init__.py", + ] + + # Compare pyproject.toml + new_pyproject_toml = tomlkit.loads( + example_pyproject_toml.read_text(encoding="utf-8") + ) + old_pyproject_toml = tomlkit.loads( + (tempdir / "pyproject.toml").read_text(encoding="utf-8") + ) + + old_pyproject_toml["tool"]["poetry"].pop("version") # type: ignore[attr-defined] + new_version = new_pyproject_toml["tool"]["poetry"].pop( # type: ignore[attr-defined] # type: ignore[attr-defined] + "version" + ) + + assert old_pyproject_toml == new_pyproject_toml + assert new_version == expected_new_version + + # Compare __init__.py + new_init_py = ( + (example_project / "src" / EXAMPLE_PROJECT_NAME / "__init__.py") + .read_text(encoding="utf-8") + .splitlines(keepends=True) + ) + old_init_py = ( + (tempdir / "src" / EXAMPLE_PROJECT_NAME / "__init__.py") + .read_text(encoding="utf-8") + .splitlines(keepends=True) + ) + + d = difflib.Differ() + diff = list(d.compare(old_init_py, new_init_py)) + added = [line[2:] for line in diff if line.startswith("+ ")] + removed = [line[2:] for line in diff if line.startswith("- ")] + + assert len(removed) == 1 + assert re.match('__version__ = ".*"', removed[0]) + assert added == [f'__version__ = "{expected_new_version}"\n'] From c94fb6f53bd8bdeaa4f40219886fa7c6e8755f29 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 7 Dec 2023 16:48:11 +0000 Subject: [PATCH 007/167] style: beautify de6b9ad921e697b5ea2bb2ea8f180893cecca920 --- semantic_release/cli/commands/version.py | 2 +- tests/command_line/test_version.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index f1793cf2c..8be43ac29 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -153,7 +153,7 @@ def shell(cmd: str, *, check: bool = True) -> subprocess.CompletedProcess: "--tag/--no-tag", "create_tag", default=True, - help="Whether or not to create a tag for the new version" + help="Whether or not to create a tag for the new version", ) @click.option( "--changelog/--no-changelog", diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 627f38678..2b39636b2 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -624,13 +624,15 @@ def test_version_only_update_files_no_git_actions( runtime_context_with_tags: RuntimeContext, cli_runner: CliRunner, tmp_path_factory: pytest.TempPathFactory, - example_pyproject_toml: Path + example_pyproject_toml: Path, ) -> None: # Arrange expected_new_version = "0.3.0" tempdir = tmp_path_factory.mktemp("test_version") shutil.rmtree(str(tempdir.resolve())) - example_project = Path(runtime_context_with_tags.repo.git.rev_parse("--show-toplevel")) + example_project = Path( + runtime_context_with_tags.repo.git.rev_parse("--show-toplevel") + ) shutil.copytree(src=str(example_project.resolve()), dst=tempdir) head_before = runtime_context_with_tags.repo.head.commit @@ -646,7 +648,9 @@ def test_version_only_update_files_no_git_actions( # Assert assert tags_before == tags_after assert head_before == head_after - assert mocked_git_push.call_count == 0 # no push as it should be turned off automatically + assert ( + mocked_git_push.call_count == 0 + ) # no push as it should be turned off automatically assert resp.exit_code == 0, ( "Unexpected failure in command " f"'semantic-release {str.join(' ', args)}': " + resp.stderr From aa9b33b9e4001f9d9071797e16f144a9f6b90fb5 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Thu, 7 Dec 2023 16:50:10 +0000 Subject: [PATCH 008/167] 8.4.0 Automatically generated by python-semantic-release --- CHANGELOG.md | 1206 ++++++++++++++++++---------------- pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 641 insertions(+), 569 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36ed3a9cc..cc63c7df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,78 @@ +## v8.4.0 (2023-12-07) + +### Build + +* build(deps-dev): bump ruff from 0.1.2 to 0.1.6 (#757) + +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.2 to 0.1.6. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.2...v0.1.6) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> +Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`90db8f1`](https://github.com/python-semantic-release/python-semantic-release/commit/90db8f1bd8986eda1b913cd4bab5abd41192f01f)) + +* build(deps-dev): update python-gitlab requirement from <4,>=2 to >=2,<5 (#748) + +Updates the requirements on [python-gitlab](https://github.com/python-gitlab/python-gitlab) to permit the latest version. +- [Release notes](https://github.com/python-gitlab/python-gitlab/releases) +- [Changelog](https://github.com/python-gitlab/python-gitlab/blob/main/CHANGELOG.md) +- [Commits](https://github.com/python-gitlab/python-gitlab/compare/v2.0.0...v4.1.1) + +--- +updated-dependencies: +- dependency-name: python-gitlab + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> +Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`a176d62`](https://github.com/python-semantic-release/python-semantic-release/commit/a176d626f28ba68ae8a938b2f04f74da841a7eeb)) + +* build(deps-dev): bump ruff from 0.0.292 to 0.1.1 ([`9c5bbe0`](https://github.com/python-semantic-release/python-semantic-release/commit/9c5bbe0b0ef96e0fadae9e65918fc8939d0d3e60)) + +### Documentation + +* docs(migration): fix comments about publish command (#747) ([`90380d7`](https://github.com/python-semantic-release/python-semantic-release/commit/90380d797a734dcca5040afc5fa00e3e01f64152)) + +### Feature + +* feat(cmd-version): add `--tag/--no-tag` option to version command (#752) + +* fix(version): separate push tags from commit push when not committing changes + +* feat(version): add `--no-tag` option to turn off tag creation + +* test(version): add test for `--tag` option & `--no-tag/commit` + +* docs(commands): update `version` subcommand options ([`de6b9ad`](https://github.com/python-semantic-release/python-semantic-release/commit/de6b9ad921e697b5ea2bb2ea8f180893cecca920)) + +### Style + +* style: beautify de6b9ad921e697b5ea2bb2ea8f180893cecca920 ([`c94fb6f`](https://github.com/python-semantic-release/python-semantic-release/commit/c94fb6f53bd8bdeaa4f40219886fa7c6e8755f29)) + +* style: convert formatter from black to ruff (#746) ([`deb4dba`](https://github.com/python-semantic-release/python-semantic-release/commit/deb4dbaba3de5396e0eb2389e728d0b1fe702843)) + +### Test + +* test(commandline-main): prevent git gpgsign config from failing tests (#760) ([`744ff25`](https://github.com/python-semantic-release/python-semantic-release/commit/744ff256f10c895a97241dc085bc132d10c9f737)) + +### Unknown + +* Revert "feat(action): use composite action for semantic release (#692)" + +This reverts commit 4648d87bac8fb7e6cc361b765b4391b30a8caef8. ([`f145257`](https://github.com/python-semantic-release/python-semantic-release/commit/f1452578cc064edbe64d61ae3baab4bc9bd4b666)) + + ## v8.3.0 (2023-10-23) ### Feature @@ -57,19 +129,19 @@ GitHub.upload_asset now raises ValueError instead of requests.HTTPError ([`a13a6 * build(deps-dev): bump ruff from 0.0.286 to 0.0.290 (#713) -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.286 to 0.0.290. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.286...v0.0.290) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.286 to 0.0.290. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.286...v0.0.290) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`6b288f2`](https://github.com/python-semantic-release/python-semantic-release/commit/6b288f2033366a8a2d6e730938df3606fa5ca5a7)) ### Fix @@ -83,12 +155,12 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 (#704) -* build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 - -* build(deps-dev): relax importlib-resources requirement - ---------- - +* build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 + +* build(deps-dev): relax importlib-resources requirement + +--------- + Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`861fe01`](https://github.com/python-semantic-release/python-semantic-release/commit/861fe0119131f69761ae3e06ec46d62e526269d6)) ### Chore @@ -134,50 +206,50 @@ Co-authored-by: github-actions <action@github.com> ([`8a515ca`](https://gi * build(deps-dev): update pytest-xdist requirement (#677) -Updates the requirements on [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) to permit the latest version. -- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst) -- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.0.0...v3.3.1) - ---- -updated-dependencies: -- dependency-name: pytest-xdist - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) to permit the latest version. +- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst) +- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.0.0...v3.3.1) + +--- +updated-dependencies: +- dependency-name: pytest-xdist + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`9ec6321`](https://github.com/python-semantic-release/python-semantic-release/commit/9ec63215c8d60230ff90c06096d8f37956bd156a)) * build(deps-dev): update coverage[toml] requirement (#678) -Updates the requirements on [coverage[toml]](https://github.com/nedbat/coveragepy) to permit the latest version. -- [Release notes](https://github.com/nedbat/coveragepy/releases) -- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) -- [Commits](https://github.com/nedbat/coveragepy/compare/6.0...7.3.0) - ---- -updated-dependencies: -- dependency-name: coverage[toml] - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [coverage[toml]](https://github.com/nedbat/coveragepy) to permit the latest version. +- [Release notes](https://github.com/nedbat/coveragepy/releases) +- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) +- [Commits](https://github.com/nedbat/coveragepy/compare/6.0...7.3.0) + +--- +updated-dependencies: +- dependency-name: coverage[toml] + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`3561a97`](https://github.com/python-semantic-release/python-semantic-release/commit/3561a977fc56ec4bb12c9a894c0072cd8a3519fa)) * build(deps-dev): bump responses from 0.21.0 to 0.23.3 (#680) -Bumps [responses](https://github.com/getsentry/responses) from 0.21.0 to 0.23.3. -- [Release notes](https://github.com/getsentry/responses/releases) -- [Changelog](https://github.com/getsentry/responses/blob/master/CHANGES) -- [Commits](https://github.com/getsentry/responses/compare/0.21.0...0.23.3) - ---- -updated-dependencies: -- dependency-name: responses - dependency-type: direct:production - update-type: version-update:semver-minor -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [responses](https://github.com/getsentry/responses) from 0.21.0 to 0.23.3. +- [Release notes](https://github.com/getsentry/responses/releases) +- [Changelog](https://github.com/getsentry/responses/blob/master/CHANGES) +- [Commits](https://github.com/getsentry/responses/compare/0.21.0...0.23.3) + +--- +updated-dependencies: +- dependency-name: responses + dependency-type: direct:production + update-type: version-update:semver-minor +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`dd6946c`](https://github.com/python-semantic-release/python-semantic-release/commit/dd6946cd479f646f3af028b92ca83db8fc4c0c66)) ### Chore @@ -207,8 +279,8 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * fix: don't warn about vcs token if ignore_token_for_push is true. (#670) -* fix: don't warn about vcs token if ignore_token_for_push is true. - +* fix: don't warn about vcs token if ignore_token_for_push is true. + * docs: `password` should be `token`. ([`f1a54a6`](https://github.com/python-semantic-release/python-semantic-release/commit/f1a54a6c9a05b225b6474d50cd610eca19ec0c34)) ### Style @@ -274,184 +346,184 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * feat!: v8 (#619) -* feat!: 8.0.x (#538) - -Co-authored-by: Johan <johanhmr@gmail.com> -Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> - -* fix: correct Dockerfile CLI command and GHA fetch - -* fix: resolve branch checkout logic in GHA - -* fix: remove commit amending behaviour - -this was not working when there were no source code changes to be made, as it lead -to attempting to amend a HEAD commit that wasn't produced by PSR - -* 8.0.0-alpha.1 - -Automatically generated by python-semantic-release - -* fix: correct logic for generating release notes (#550) - -* fix: cleanup comments and unused logic - -* fix(action): mark container fs as safe for git to operate on - -* style: beautify 49080c510a68cccd2f6c7a8d540b483751901207 - -* fix(action): quotation for git config command - -* 8.0.0-alpha.2 - -Automatically generated by python-semantic-release - -* fix: resolve bug in changelog logic, enable upload to pypi - -* 8.0.0-alpha.3 - -Automatically generated by python-semantic-release - -* test: add tests for ReleaseHistory.release - -* fix: resolve loss of tag_format configuration - -* 8.0.0-alpha.4 - -Automatically generated by python-semantic-release - -* feat: various improvements - -* Added sorting to test parameterisation, so that pytest-xdist works again - dramatic speedup for testing -* Reworked the CI verification code so it's a bit prettier -* Added more testing for the version CLI command, and split some logic out of the command itself -* Removed a redundant double-regex match in VersionTranslator and Version, for some speedup - -* chore(test): proper env patching for tests in CI - -* style: beautify bcb27a4a8ce4789d083226f088c1810f45cd4c77 - -* refactor!: remove verify-ci command - -* 8.0.0-alpha.5 - -Automatically generated by python-semantic-release - -* fix(docs): fixup docs and remove reference to dist publication - -* feat!: remove publication of dists to artefact repository - -* feat: rename 'upload' configuration section to 'publish' - -* feat!: removed build status checking - -* feat: add GitHub Actions output - -* fix(action): remove default for 'force' - -* fix(ci): different workflow for v8 - -* fix(action): correct input parsing - -* fix: correct handling of build commands - -* feat: make it easier to access commit messages in ParsedCommits - -* fix: make additional attributes available for template authors - -* fix: add logging for token auth, use token for push - -* ci: add verbosity - -* fix: caching for repo owner and name - -* ci: contents permission for workflow - -* 8.0.0-alpha.6 - -Automatically generated by python-semantic-release - -* docs: update docs with additional required permissions - -* feat: add option to specify tag to publish to in publish command - -* feat: add Strict Mode - -* docs: convert to Furo theme - -* feat: add --skip-build option - -* 8.0.0-alpha.7 - -Automatically generated by python-semantic-release - -* test: separate command line tests by stdout and stderr - -* ci: pass tag output and conditionally execute publish steps - -* fix: correct assets type in configuration (#603) - -* change raw config assets type - -* fix: correct assets type-annotation for RuntimeContext - ---------- - -Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> - -* 8.0.0-alpha.8 - -Automatically generated by python-semantic-release - -* fix: pin Debian version in Dockerfile - -* feat: promote to rc - -* 8.0.0-rc.1 - -Automatically generated by python-semantic-release - -* ci: fix conditionals in workflow and update documentation - -* ci: correct conditionals - -* fix: only call Github Action output callback once defaults are set - -* 8.0.0-rc.2 - -Automatically generated by python-semantic-release - -* fix: create_or_update_release for Gitlab hvcs - -* ci: remove separate v8 workflow - -* chore: tweak issue templates - -* chore: bump docs dependencies - -* 8.0.0-rc.3 - -Automatically generated by python-semantic-release - -* fix(deps): add types-click, and downgrade sphinx/furo for 3.7 - -* 8.0.0-rc.4 - -Automatically generated by python-semantic-release - -* docs: fix typo (#623) - -* docs: correct typo in docs/changelog_templates.rst - -Co-authored-by: Micael Jarniac <micael@jarniac.com> - ---------- - -Co-authored-by: Johan <johanhmr@gmail.com> -Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> -Co-authored-by: semantic-release <semantic-release> -Co-authored-by: github-actions <action@github.com> -Co-authored-by: smeng9 <38666763+smeng9@users.noreply.github.com> +* feat!: 8.0.x (#538) + +Co-authored-by: Johan <johanhmr@gmail.com> +Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> + +* fix: correct Dockerfile CLI command and GHA fetch + +* fix: resolve branch checkout logic in GHA + +* fix: remove commit amending behaviour + +this was not working when there were no source code changes to be made, as it lead +to attempting to amend a HEAD commit that wasn't produced by PSR + +* 8.0.0-alpha.1 + +Automatically generated by python-semantic-release + +* fix: correct logic for generating release notes (#550) + +* fix: cleanup comments and unused logic + +* fix(action): mark container fs as safe for git to operate on + +* style: beautify 49080c510a68cccd2f6c7a8d540b483751901207 + +* fix(action): quotation for git config command + +* 8.0.0-alpha.2 + +Automatically generated by python-semantic-release + +* fix: resolve bug in changelog logic, enable upload to pypi + +* 8.0.0-alpha.3 + +Automatically generated by python-semantic-release + +* test: add tests for ReleaseHistory.release + +* fix: resolve loss of tag_format configuration + +* 8.0.0-alpha.4 + +Automatically generated by python-semantic-release + +* feat: various improvements + +* Added sorting to test parameterisation, so that pytest-xdist works again - dramatic speedup for testing +* Reworked the CI verification code so it's a bit prettier +* Added more testing for the version CLI command, and split some logic out of the command itself +* Removed a redundant double-regex match in VersionTranslator and Version, for some speedup + +* chore(test): proper env patching for tests in CI + +* style: beautify bcb27a4a8ce4789d083226f088c1810f45cd4c77 + +* refactor!: remove verify-ci command + +* 8.0.0-alpha.5 + +Automatically generated by python-semantic-release + +* fix(docs): fixup docs and remove reference to dist publication + +* feat!: remove publication of dists to artefact repository + +* feat: rename 'upload' configuration section to 'publish' + +* feat!: removed build status checking + +* feat: add GitHub Actions output + +* fix(action): remove default for 'force' + +* fix(ci): different workflow for v8 + +* fix(action): correct input parsing + +* fix: correct handling of build commands + +* feat: make it easier to access commit messages in ParsedCommits + +* fix: make additional attributes available for template authors + +* fix: add logging for token auth, use token for push + +* ci: add verbosity + +* fix: caching for repo owner and name + +* ci: contents permission for workflow + +* 8.0.0-alpha.6 + +Automatically generated by python-semantic-release + +* docs: update docs with additional required permissions + +* feat: add option to specify tag to publish to in publish command + +* feat: add Strict Mode + +* docs: convert to Furo theme + +* feat: add --skip-build option + +* 8.0.0-alpha.7 + +Automatically generated by python-semantic-release + +* test: separate command line tests by stdout and stderr + +* ci: pass tag output and conditionally execute publish steps + +* fix: correct assets type in configuration (#603) + +* change raw config assets type + +* fix: correct assets type-annotation for RuntimeContext + +--------- + +Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> + +* 8.0.0-alpha.8 + +Automatically generated by python-semantic-release + +* fix: pin Debian version in Dockerfile + +* feat: promote to rc + +* 8.0.0-rc.1 + +Automatically generated by python-semantic-release + +* ci: fix conditionals in workflow and update documentation + +* ci: correct conditionals + +* fix: only call Github Action output callback once defaults are set + +* 8.0.0-rc.2 + +Automatically generated by python-semantic-release + +* fix: create_or_update_release for Gitlab hvcs + +* ci: remove separate v8 workflow + +* chore: tweak issue templates + +* chore: bump docs dependencies + +* 8.0.0-rc.3 + +Automatically generated by python-semantic-release + +* fix(deps): add types-click, and downgrade sphinx/furo for 3.7 + +* 8.0.0-rc.4 + +Automatically generated by python-semantic-release + +* docs: fix typo (#623) + +* docs: correct typo in docs/changelog_templates.rst + +Co-authored-by: Micael Jarniac <micael@jarniac.com> + +--------- + +Co-authored-by: Johan <johanhmr@gmail.com> +Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> +Co-authored-by: semantic-release <semantic-release> +Co-authored-by: github-actions <action@github.com> +Co-authored-by: smeng9 <38666763+smeng9@users.noreply.github.com> Co-authored-by: Micael Jarniac <micael@jarniac.com> ([`ec30564`](https://github.com/python-semantic-release/python-semantic-release/commit/ec30564b4ec732c001d76d3c09ba033066d2b6fe)) @@ -475,11 +547,11 @@ Co-authored-by: Micael Jarniac <micael@jarniac.com> ([`ec30564`](https://g * fix: docker build fails installing git (#605) -git was installed from bullseye-backports, but base image is referencing latest python:3.10 -since bookworm was recently released, this now points at bookworm and installing the backport of git is actually trying to downgrade, resulting in this error: - -> E: Packages were downgraded and -y was used without --allow-downgrades. - +git was installed from bullseye-backports, but base image is referencing latest python:3.10 +since bookworm was recently released, this now points at bookworm and installing the backport of git is actually trying to downgrade, resulting in this error: + +> E: Packages were downgraded and -y was used without --allow-downgrades. + > ERROR: failed to solve: process "/bin/sh -c echo \"deb http://deb.debian.org/debian bullseye-backports main\" >> /etc/apt/sources.list; apt-get update; apt-get install -y git/bullseye-backports" did not complete successfully: exit code: 100 ([`9e3eb97`](https://github.com/python-semantic-release/python-semantic-release/commit/9e3eb979783bc39ca564c2967c6c77eecba682e6)) @@ -505,7 +577,7 @@ In #594, I missed that there are 2 places where the version header is formatted * fix: generate markdown linter compliant changelog headers & lists (#594) -Add an extra new line after each header and between sections to fix 2 markdownlint errors +Add an extra new line after each header and between sections to fix 2 markdownlint errors for changelogs generated by this package ([`9d9d403`](https://github.com/python-semantic-release/python-semantic-release/commit/9d9d40305c499c907335abe313e3ed122db0b154)) ### Style @@ -519,9 +591,9 @@ for changelogs generated by this package ([`9d9d403`](https://github.com/python- * feat: add option to only parse commits for current working directory (#509) -When running the application from a sub-directory in the VCS, the option -use_only_cwd_commits will filter out commits that does not changes the -current working directory, similar to running commands like +When running the application from a sub-directory in the VCS, the option +use_only_cwd_commits will filter out commits that does not changes the +current working directory, similar to running commands like `git log -- .` in a sub-directory. ([`cdf8116`](https://github.com/python-semantic-release/python-semantic-release/commit/cdf8116c1e415363b10a01f541873e04ad874220)) @@ -531,22 +603,22 @@ current working directory, similar to running commands like * docs: update broken badge and add links (#591) -The "Test Status" badge was updated to address a recent breaking change in the -GitHub actions API. All the badges updated to add links to the appropriate +The "Test Status" badge was updated to address a recent breaking change in the +GitHub actions API. All the badges updated to add links to the appropriate resources for end-user convenience. ([`0c23447`](https://github.com/python-semantic-release/python-semantic-release/commit/0c234475d27ad887b19170c82deb80293b3a95f1)) ### Fix * fix: update docs and default config for gitmoji changes (#590) -* fix: update docs and default config for gitmoji changes - -PR #582 updated to the latest Gitmojis release however the documentation and -default config options still referenced old and unsupported Gitmojis. - -* fix: update sphinx dep - -I could only build the documentation locally by updating Sphinx to the latest +* fix: update docs and default config for gitmoji changes + +PR #582 updated to the latest Gitmojis release however the documentation and +default config options still referenced old and unsupported Gitmojis. + +* fix: update sphinx dep + +I could only build the documentation locally by updating Sphinx to the latest 1.x version. ([`192da6e`](https://github.com/python-semantic-release/python-semantic-release/commit/192da6e1352298b48630423d50191070a1c5ab24)) @@ -573,16 +645,16 @@ In order to avoid 'Repository not found: relekang/python-semantic-release.&# * docs: spelling and grammar in `travis.rst` (#556) -- spelling -- subject-verb agreement -- remove verbiage - +- spelling +- subject-verb agreement +- remove verbiage + Signed-off-by: Vladislav Doster <mvdoster@gmail.com> ([`3a76e9d`](https://github.com/python-semantic-release/python-semantic-release/commit/3a76e9d7505c421009eb3e953c32cccac2e70e07)) * docs: grammar in `docs/troubleshooting.rst` (#557) -- change contraction to a possessive pronoun - +- change contraction to a possessive pronoun + Signed-off-by: Vladislav Doster <mvdoster@gmail.com> ([`bbe754a`](https://github.com/python-semantic-release/python-semantic-release/commit/bbe754a3db9ce7132749e7902fe118b52f48ee42)) ### Fix @@ -630,11 +702,11 @@ It is spamming to much. We can bring it back if we get the time to fix the spamm * ci: remove python3.6 from GHA, add python3.10 and python3.11 (#541) -* ci: remove python3.6 from GHA, add python3.10 and python3.11 - -GHA workflows are failing without this, due to -https://github.com/actions/setup-python/issues/544\#issuecomment-1332535877 - +* ci: remove python3.6 from GHA, add python3.10 and python3.11 + +GHA workflows are failing without this, due to +https://github.com/actions/setup-python/issues/544\#issuecomment-1332535877 + * fix: upgrade pytest ([`8e4aa0e`](https://github.com/python-semantic-release/python-semantic-release/commit/8e4aa0e30438291ade98604a18aeb372f0d0b52f)) ### Feature @@ -653,17 +725,17 @@ Co-authored-by: Kevin Watson <Kevmo92@users.noreply.github.com> ([`4664afe * fix: changelog release commit search logic (#530) -* Fixes changelog release commit search logic - -Running `semantic-release changelog` currently fails to identify "the last commit in [a] release" because the compared commit messages have superfluous whitespace. -Likely related to the issue causing: https://github.com/relekang/python-semantic-release/issues/490 - +* Fixes changelog release commit search logic + +Running `semantic-release changelog` currently fails to identify "the last commit in [a] release" because the compared commit messages have superfluous whitespace. +Likely related to the issue causing: https://github.com/relekang/python-semantic-release/issues/490 + * Removes a couple of extra `strip()`s. ([`efb3410`](https://github.com/python-semantic-release/python-semantic-release/commit/efb341036196c39b4694ca4bfa56c6b3e0827c6c)) * fix: bump Dockerfile to use Python 3.10 image (#536) -Fixes #533 - +Fixes #533 + Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`8f2185d`](https://github.com/python-semantic-release/python-semantic-release/commit/8f2185d570b3966b667ac591ae523812e9d2e00f)) * fix: fix mypy errors for publish ([`b40dd48`](https://github.com/python-semantic-release/python-semantic-release/commit/b40dd484387c1b3f78df53ee2d35e281e8e799c8)) @@ -689,10 +761,10 @@ Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`8f2185d`](http * ci: Update deprecated actions (#511) -* ci: update depreated actions - -* ci: replace deprecated set-output in workflow - +* ci: update depreated actions + +* ci: replace deprecated set-output in workflow + According to https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ ([`bb09233`](https://github.com/python-semantic-release/python-semantic-release/commit/bb09233b84d153a15784fdf68d7274c9d682c336)) ### Documentation @@ -863,9 +935,9 @@ Fixes #473 ([`0ece6f2`](https://github.com/python-semantic-release/python-semant * fix: allow changing prerelease tag using CLI flags (#466) -Delay construction of version and release patterns until runtime. -This will allow to use non-default prerelease tag. - +Delay construction of version and release patterns until runtime. +This will allow to use non-default prerelease tag. + Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`395bf4f`](https://github.com/python-semantic-release/python-semantic-release/commit/395bf4f2de73663c070f37cced85162d41934213)) @@ -875,8 +947,8 @@ Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`395bf4f`](http * fix(publish): get version bump for current release (#467) -Replicate the behavior of "version" command in version calculation. - +Replicate the behavior of "version" command in version calculation. + Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`dd26888`](https://github.com/python-semantic-release/python-semantic-release/commit/dd26888a923b2f480303c19f1916647de48b02bf)) * fix: add packaging module requirement (#469) ([`b99c9fa`](https://github.com/python-semantic-release/python-semantic-release/commit/b99c9fa88dc25e5ceacb131cd93d9079c4fb2c86)) @@ -950,33 +1022,33 @@ Co-authored-by: Sebastian Seith <sebastian@vermill.io> ([`da0606f`](https: * ci: adjust actions test phase to use fetch-depth: 0 to fix ci tests (#446) -Co-authored-by: Sebastian Seith <sebastian@vermill.io> +Co-authored-by: Sebastian Seith <sebastian@vermill.io> Co-authored-by: github-actions <action@github.com> ([`3329eef`](https://github.com/python-semantic-release/python-semantic-release/commit/3329eeffb077f628e4a965bc7fd922d09d6b63da)) ### Feature * feat: allow using ssh-key to push version while using token to publish to hvcs (#419) -* feat(config): add ignore_token_for_push param - -Add ignore_token_for_push parameter that allows using the underlying -git authentication mechanism for pushing a new version commit and tags -while also using an specified token to upload dists - -* test(config): add test for ignore_token_for_push - -Test push_new_version with token while ignore_token_for_push is True -and False - -* docs: add documentation for ignore_token_for_push - -* fix(test): override GITHUB_ACTOR env - -push_new_version is using GITHUB_ACTOR env var but we did not -contemplate in our new tests that actually Github actions running the -tests will populate that var and change the test outcome - -Now we control the value of that env var and test for it being present +* feat(config): add ignore_token_for_push param + +Add ignore_token_for_push parameter that allows using the underlying +git authentication mechanism for pushing a new version commit and tags +while also using an specified token to upload dists + +* test(config): add test for ignore_token_for_push + +Test push_new_version with token while ignore_token_for_push is True +and False + +* docs: add documentation for ignore_token_for_push + +* fix(test): override GITHUB_ACTOR env + +push_new_version is using GITHUB_ACTOR env var but we did not +contemplate in our new tests that actually Github actions running the +tests will populate that var and change the test outcome + +Now we control the value of that env var and test for it being present or not ([`7b2dffa`](https://github.com/python-semantic-release/python-semantic-release/commit/7b2dffadf43c77d5e0eea307aefcee5c7744df5c)) ### Fix @@ -1014,16 +1086,16 @@ Fixes #354 ([`cf74339`](https://github.com/python-semantic-release/python-semant * chore(dependencies): unpin tomlkit dependency (#429) -- tests for a tomlkit regression don't fail anymore with newer tomlkit -- keep comment in setup.py about tomlkit being pinned at some point in time - +- tests for a tomlkit regression don't fail anymore with newer tomlkit +- keep comment in setup.py about tomlkit being pinned at some point in time + refs #336 ([`8515879`](https://github.com/python-semantic-release/python-semantic-release/commit/85158798ca438c1dafc84036d13c2988c934f02f)) ### Fix * fix(prerelase): pass prerelease option to get_current_version (#432) -The `get_current_version` function accepts a `prerelease` argument which +The `get_current_version` function accepts a `prerelease` argument which was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python-semantic-release/commit/aabab0b7ce647d25e0c78ae6566f1132ece9fcb9)) ### Style @@ -1037,7 +1109,7 @@ was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python * chore(dependencies): extend allowed version range for python-gitlab (#417) -* chore(dependencies): extend allowed version range for python-gitlab +* chore(dependencies): extend allowed version range for python-gitlab * fix(type): ignore mypy errors for dynamic RESTObject ([`8ee4d4b`](https://github.com/python-semantic-release/python-semantic-release/commit/8ee4d4b8dabfa5c6cd2aa6180d4a8da8f3c9554c)) ### Feature @@ -1051,18 +1123,18 @@ was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python * feat: add prerelease functionality (#413) -* chore: add initial todos -* feat: add prerelease tag option -* feat: add prerelease cli flag -* feat: omit_pattern for previouse and current version getters -* feat: print_version with prerelease bump -* feat: make print_version prerelease ready -* feat: move prerelease determination to get_new_version -* test: improve get_last_version test -* docs: added basic infos about prereleases -* feat: add prerelease flag to version and publish -* feat: remove leftover todos - +* chore: add initial todos +* feat: add prerelease tag option +* feat: add prerelease cli flag +* feat: omit_pattern for previouse and current version getters +* feat: print_version with prerelease bump +* feat: make print_version prerelease ready +* feat: move prerelease determination to get_new_version +* test: improve get_last_version test +* docs: added basic infos about prereleases +* feat: add prerelease flag to version and publish +* feat: remove leftover todos + Co-authored-by: Mario Jäckle <m.jaeckle@careerpartner.eu> ([`7064265`](https://github.com/python-semantic-release/python-semantic-release/commit/7064265627a2aba09caa2873d823b594e0e23e77)) ### Style @@ -1083,7 +1155,7 @@ Co-authored-by: Mario Jäckle <m.jaeckle@careerpartner.eu> ([`7064265`](ht * fix(gitea): build status and asset upload (#420) -* fix(gitea): handle list build status response +* fix(gitea): handle list build status response * fix(gitea): use form-data for upload_asset ([`57db81f`](https://github.com/python-semantic-release/python-semantic-release/commit/57db81f4c6b96da8259e3bad9137eaccbcd10f6e)) ### Style @@ -1215,10 +1287,10 @@ executable. No-op mode now respected by artifact upload. ([`cfb20af`](https://gi * feat: allow custom environment variable names (#392) -* GH_TOKEN can now be customized by setting github_token_var -* GL_TOKEN can now be customized by setting gitlab_token_var -* PYPI_PASSWORD can now be customized by setting pypi_pass_var -* PYPI_TOKEN can now be customized by setting pypi_token_var +* GH_TOKEN can now be customized by setting github_token_var +* GL_TOKEN can now be customized by setting gitlab_token_var +* PYPI_PASSWORD can now be customized by setting pypi_pass_var +* PYPI_TOKEN can now be customized by setting pypi_token_var * PYPI_USERNAME can now be customized by setting pypi_user_var ([`372cda3`](https://github.com/python-semantic-release/python-semantic-release/commit/372cda3497f16ead2209e6e1377d38f497144883)) * feat: use gitlab-ci or github actions env vars @@ -1286,8 +1358,8 @@ https://github.com/relekang/python-semantic-release/issues/391#issuecomment-9506 * feat: custom git tag format support (#373) -* feat: custom git tag format support -* test: add git tag format check +* feat: custom git tag format support +* test: add git tag format check * docs: add tag_format config option ([`1d76632`](https://github.com/python-semantic-release/python-semantic-release/commit/1d76632043bf0b6076d214a63c92013624f4b95e)) @@ -1333,9 +1405,9 @@ Co-authored-by: Laercio Barbosa <laercio.barbosa@scania.com> ([`a275a7a`]( * chore: bump responses to latest version (#343) -The current version has a Deprecation warning: - -inspect.getargspec() is deprecated since Python 3.0, +The current version has a Deprecation warning: + +inspect.getargspec() is deprecated since Python 3.0, use inspect.signature() or inspect.getfullargspec() ([`e953157`](https://github.com/python-semantic-release/python-semantic-release/commit/e953157125f4528759453218f75b6e51cafd2cc3)) ### Ci @@ -1518,23 +1590,23 @@ Fixes #329 ([`abfacc4`](https://github.com/python-semantic-release/python-semant * docs: correct casing on proper nouns (#320) -* docs: correcting Semantic Versioning casing - -Semantic Versioning is the name of the specification. -Therefore it is a proper noun. -This patch corrects the incorrect casing for Semantic Versioning. - -* docs: correcting Python casing - +* docs: correcting Semantic Versioning casing + +Semantic Versioning is the name of the specification. +Therefore it is a proper noun. +This patch corrects the incorrect casing for Semantic Versioning. + +* docs: correcting Python casing + This patch corrects the incorrect casing for Python. ([`d51b999`](https://github.com/python-semantic-release/python-semantic-release/commit/d51b999a245a4e56ff7a09d0495c75336f2f150d)) ### Feature * feat(checks): add support for Jenkins CI (#322) -Includes a ci check handler to verify jenkins. -Unlike other ci systems jenkins doesn't generally prefix things with -`JENKINS` or simply inject `JENKINS=true` Really the only thing that is +Includes a ci check handler to verify jenkins. +Unlike other ci systems jenkins doesn't generally prefix things with +`JENKINS` or simply inject `JENKINS=true` Really the only thing that is immediately identifiable is `JENKINS_URL` ([`3e99855`](https://github.com/python-semantic-release/python-semantic-release/commit/3e99855c6bc72b3e9a572c58cc14e82ddeebfff8)) ### Style @@ -1552,10 +1624,10 @@ immediately identifiable is `JENKINS_URL` ([`3e99855`](https://github.com/python * fix: fix crash when TOML has no PSR section (#319) -* test: reproduce issue with TOML without PSR section - -* fix: crash when TOML has no PSR section - +* test: reproduce issue with TOML without PSR section + +* fix: crash when TOML has no PSR section + * chore: remove unused imports ([`5f8ab99`](https://github.com/python-semantic-release/python-semantic-release/commit/5f8ab99bf7254508f4b38fcddef2bdde8dd15a4c)) @@ -1578,26 +1650,26 @@ Fixes #306 ([`1a85af4`](https://github.com/python-semantic-release/python-semant * feat: support toml files for version declaration (#307) -This introduce a new `version_toml` configuration property that behaves -like `version_pattern` and `version_variable`. - -For poetry support, user should now set `version_toml = pyproject.toml:tool.poetry.version`. - -This introduce an ABC class, `VersionDeclaration`, that -can be implemented to add other version declarations with ease. - -`toml` dependency has been replaced by `tomlkit`, as this is used -the library used by poetry to generate the `pyproject.toml` file, and -is able to keep the ordering of data defined in the file. - -Existing `VersionPattern` class has been renamed to -`PatternVersionDeclaration` and now implements `VersionDeclaration`. - -`load_version_patterns()` function has been renamed to -`load_version_declarations()` and now return a list of -`VersionDeclaration` implementations. - -Close #245 +This introduce a new `version_toml` configuration property that behaves +like `version_pattern` and `version_variable`. + +For poetry support, user should now set `version_toml = pyproject.toml:tool.poetry.version`. + +This introduce an ABC class, `VersionDeclaration`, that +can be implemented to add other version declarations with ease. + +`toml` dependency has been replaced by `tomlkit`, as this is used +the library used by poetry to generate the `pyproject.toml` file, and +is able to keep the ordering of data defined in the file. + +Existing `VersionPattern` class has been renamed to +`PatternVersionDeclaration` and now implements `VersionDeclaration`. + +`load_version_patterns()` function has been renamed to +`load_version_declarations()` and now return a list of +`VersionDeclaration` implementations. + +Close #245 Close #275 ([`9b62a7e`](https://github.com/python-semantic-release/python-semantic-release/commit/9b62a7e377378667e716384684a47cdf392093fa)) ### Style @@ -1617,8 +1689,8 @@ Fixes #311 ([`e2d8e47`](https://github.com/python-semantic-release/python-semant * feat(github): retry GitHub API requests on failure (#314) -* refactor(github): use requests.Session to call raise_for_status - +* refactor(github): use requests.Session to call raise_for_status + * fix(github): add retries to github API requests ([`ac241ed`](https://github.com/python-semantic-release/python-semantic-release/commit/ac241edf4de39f4fc0ff561a749fa85caaf9e2ae)) ### Style @@ -1712,10 +1784,10 @@ Fixes: #277 ([`ab3061a`](https://github.com/python-semantic-release/python-seman * fix(history): coerce version to string (#298) -The changes in #297 mistakenly omitted coercing the return value to a -string. This resulted in errors like: -"can only concatenate str (not "VersionInfo") to str" - +The changes in #297 mistakenly omitted coercing the return value to a +string. This resulted in errors like: +"can only concatenate str (not "VersionInfo") to str" + Add test case asserting it's type str ([`d4cdc3d`](https://github.com/python-semantic-release/python-semantic-release/commit/d4cdc3d3cd2d93f2a78f485e3ea107ac816c7d00)) * fix(history): require semver >= 2.10 @@ -1791,8 +1863,8 @@ the end of a PR that is merged via rebase merge or merge commit. ([`93e48c9`](ht * feat(changelog): add PR links in markdown (#282) -GitHub release notes automagically link to the PR, but changelog -markdown doesn't. Replace a PR number at the end of a message +GitHub release notes automagically link to the PR, but changelog +markdown doesn't. Replace a PR number at the end of a message with a markdown link. ([`0448f6c`](https://github.com/python-semantic-release/python-semantic-release/commit/0448f6c350bbbf239a81fe13dc5f45761efa7673)) ### Style @@ -1816,7 +1888,7 @@ To control if bump major or not when current major version is zero. ([`d324154`] * refactor(history): move changelog_scope default (#284) -* Move the default for changelog_scope from inline to defaults.cfg. +* Move the default for changelog_scope from inline to defaults.cfg. * Add missing header in docs. ([`b7e1376`](https://github.com/python-semantic-release/python-semantic-release/commit/b7e1376ee1688e5e6dcc069ce623f49e3a389052)) ### Style @@ -1832,13 +1904,13 @@ A few settings were not in alphabetical order. ([`60a3535`](https://github.com/p * feat(logs): include scope in changelogs (#281) -When the scope is set, include it in changelogs, e.g. -"feat(x): some description" becomes "**x**: some description". -This is similar to how the Node semantic release (and -conventional-changelog-generator) generates changelogs. -If scope is not given, it's omitted. - -Add a new config parameter changelog_scope to disable this behavior when +When the scope is set, include it in changelogs, e.g. +"feat(x): some description" becomes "**x**: some description". +This is similar to how the Node semantic release (and +conventional-changelog-generator) generates changelogs. +If scope is not given, it's omitted. + +Add a new config parameter changelog_scope to disable this behavior when set to 'False' ([`21c96b6`](https://github.com/python-semantic-release/python-semantic-release/commit/21c96b688cc44cc6f45af962ffe6d1f759783f37)) ### Style @@ -1884,11 +1956,11 @@ Fixes #260 ([`7cacca1`](https://github.com/python-semantic-release/python-semant * ci: check commit logs with commitlint (#263) -The contributing guide says that the project should itself follow the -Angular commit convention, but there is nothing to enforce it AFAIK. - -I had a similar problem on a project where I'm using -`python-semantic-release` and I've added a Github action to +The contributing guide says that the project should itself follow the +Angular commit convention, but there is nothing to enforce it AFAIK. + +I had a similar problem on a project where I'm using +`python-semantic-release` and I've added a Github action to test it on CI, you might find it useful too. ([`016fde6`](https://github.com/python-semantic-release/python-semantic-release/commit/016fde683924d380d25579bd0cff0c7f8b7b2240)) ### Documentation @@ -1913,7 +1985,7 @@ the method above. ([`5a5e2cf`](https://github.com/python-semantic-release/python * fix: add required to inputs in action metadata (#264) -According to the documentation, `inputs.<input_id>.required` is a +According to the documentation, `inputs.<input_id>.required` is a required field. ([`e76b255`](https://github.com/python-semantic-release/python-semantic-release/commit/e76b255cf7d3d156e3314fc28c54d63fa126e973)) @@ -1970,8 +2042,8 @@ Fixes #250 ([`87e2bb8`](https://github.com/python-semantic-release/python-semant * docs: give example of multiple build commands (#248) -I had a little trouble figuring out how to use a non-setup.py build -command, so I thought it would be helpful to update the docs with an +I had a little trouble figuring out how to use a non-setup.py build +command, so I thought it would be helpful to update the docs with an example of how to do this. ([`65f1ffc`](https://github.com/python-semantic-release/python-semantic-release/commit/65f1ffcc6cac3bf382f4b821ff2be59d04f9f867)) ### Fix @@ -1989,13 +2061,13 @@ example of how to do this. ([`65f1ffc`](https://github.com/python-semantic-relea * feat: bump versions in multiple files (#246) -- Add the `version_pattern` setting, which allows version numbers to be - identified using arbitrary regular expressions. -- Refactor the config system to allow non-string data types to be - specified in `pyproject.toml`. -- Multiple files can now be specified by setting `version_variable` or - `version_pattern` to a list in `pyproject.toml`. - +- Add the `version_pattern` setting, which allows version numbers to be + identified using arbitrary regular expressions. +- Refactor the config system to allow non-string data types to be + specified in `pyproject.toml`. +- Multiple files can now be specified by setting `version_variable` or + `version_pattern` to a list in `pyproject.toml`. + Fixes #175 ([`0ba2c47`](https://github.com/python-semantic-release/python-semantic-release/commit/0ba2c473c6e44cc326b3299b6ea3ddde833bdb37)) ### Style @@ -2016,9 +2088,9 @@ Fixes #175 ([`0ba2c47`](https://github.com/python-semantic-release/python-semant * feat(changelog): add changelog_table component (#242) -Add an alternative changelog component which displays each section as a -row in a table. - +Add an alternative changelog component which displays each section as a +row in a table. + Fixes #237 ([`fe6a7e7`](https://github.com/python-semantic-release/python-semantic-release/commit/fe6a7e7fa014ffb827a1430dbcc10d1fc84c886b)) ### Style @@ -2036,25 +2108,25 @@ Fixes #237 ([`fe6a7e7`](https://github.com/python-semantic-release/python-semant * feat(changelog): add changelog components (#240) -* feat(changelog): add changelog components - -Add the ability to configure sections of the changelog using a -`changelog_components` option. Component outputs are separated by a blank -line and appear in the same order as they were configured. - -It is possible to create your own custom components. Each component is a -function which returns either some text to be added, or None in which case it -will be skipped. - -BREAKING CHANGE: The `compare_url` option has been removed in favor of using -`changelog_components`. This functionality is now available as the -`semantic_release.changelog.compare_url` component. - -* docs: add documentation for changelog_components - -* feat: pass changelog_sections to components - -Changelog components may now receive the value of `changelog_sections`, +* feat(changelog): add changelog components + +Add the ability to configure sections of the changelog using a +`changelog_components` option. Component outputs are separated by a blank +line and appear in the same order as they were configured. + +It is possible to create your own custom components. Each component is a +function which returns either some text to be added, or None in which case it +will be skipped. + +BREAKING CHANGE: The `compare_url` option has been removed in favor of using +`changelog_components`. This functionality is now available as the +`semantic_release.changelog.compare_url` component. + +* docs: add documentation for changelog_components + +* feat: pass changelog_sections to components + +Changelog components may now receive the value of `changelog_sections`, split and ready to use. ([`3e17a98`](https://github.com/python-semantic-release/python-semantic-release/commit/3e17a98d7fa8468868a87e62651ac2c010067711)) ### Style @@ -2081,30 +2153,30 @@ Fixes #239 ([`34acbbc`](https://github.com/python-semantic-release/python-semant * feat(history): create emoji parser (#238) -Add a commit parser which uses emojis from https://gitmoji.carloscuesta.me/ -to determine the type of change. - -* fix: add emojis to default changelog_sections - -* fix: include all parsed types in changelog - -This allows emojis to appear in the changelog, as well as configuring -other types to appear with the Angular parser (I remember someone asking -for that feature a while ago). All filtering is now done in the -markdown_changelog function. - -* refactor(history): get breaking changes in parser - -Move the task of detecting breaking change descriptions into the commit -parser function, instead of during changelog generation. - -This has allowed the emoji parser to also return the regular descriptions as -breaking change descriptions for commits with :boom:. - -BREAKING CHANGE: Custom commit parser functions are now required to pass -a fifth argument to `ParsedCommit`, which is a list of breaking change -descriptions. - +Add a commit parser which uses emojis from https://gitmoji.carloscuesta.me/ +to determine the type of change. + +* fix: add emojis to default changelog_sections + +* fix: include all parsed types in changelog + +This allows emojis to appear in the changelog, as well as configuring +other types to appear with the Angular parser (I remember someone asking +for that feature a while ago). All filtering is now done in the +markdown_changelog function. + +* refactor(history): get breaking changes in parser + +Move the task of detecting breaking change descriptions into the commit +parser function, instead of during changelog generation. + +This has allowed the emoji parser to also return the regular descriptions as +breaking change descriptions for commits with :boom:. + +BREAKING CHANGE: Custom commit parser functions are now required to pass +a fifth argument to `ParsedCommit`, which is a list of breaking change +descriptions. + * docs: add documentation for emoji parser ([`2e1c50a`](https://github.com/python-semantic-release/python-semantic-release/commit/2e1c50a865628b372f48945a039a3edb38a7cdf0)) ### Style @@ -2178,12 +2250,12 @@ Any issues which are labelled as a question will be closed after two weeks of in * ci: pass SHA from beautify to release -Checkout the current SHA from the end of the beautify job for releasing, -instead of master. This will either be the same as the commit we are -running for, or the SHA of a style commit. This prevents releasing of +Checkout the current SHA from the end of the beautify job for releasing, +instead of master. This will either be the same as the commit we are +running for, or the SHA of a style commit. This prevents releasing of untested code. -See +See https://github.community/t5/GitHub-Actions/Checkout-commit-pushed-by-previous-job/m-p/55847#M9670 ([`76e34b6`](https://github.com/python-semantic-release/python-semantic-release/commit/76e34b6b52b8019e87eaddf295d0781b6aa51541)) ### Documentation @@ -2261,15 +2333,15 @@ This was missed in 213530fb0c914e274b81d1dacf38ea7322b5b91f ([`3084249`](https:/ * refactor(debug): use logging and click_log instead of ndebug -BREAKING CHANGE: `DEBUG="*"` no longer has an effect, instead use +BREAKING CHANGE: `DEBUG="*"` no longer has an effect, instead use `--verbosity DEBUG`. ([`15b1f65`](https://github.com/python-semantic-release/python-semantic-release/commit/15b1f650f29761e1ab2a91b767cbff79b2057a4c)) ### Build * build(pip): store requirements in setup.py -Remove the requirements directory and instead store all required -libraries directly inside setup.py. Development, testing and docs +Remove the requirements directory and instead store all required +libraries directly inside setup.py. Development, testing and docs dependencies are included as extras. ([`401468f`](https://github.com/python-semantic-release/python-semantic-release/commit/401468f312cf4f3b52006c68c58c4645b5e19802)) ### Chore @@ -2282,12 +2354,12 @@ Allow mypy and coverage to run on any Python version. ([`28feba6`](https://githu * ci: always checkout most recent commit to release -This should pull a beautify commit if one has been created, allowing the +This should pull a beautify commit if one has been created, allowing the new version to be pushed. ([`6c98aab`](https://github.com/python-semantic-release/python-semantic-release/commit/6c98aab932724e3aab08e68b75439bc8c31bd877)) * ci: cache testing dependencies -This should help improve the speed of the testing workflow by caching +This should help improve the speed of the testing workflow by caching downloaded dependencies. ([`4f53e35`](https://github.com/python-semantic-release/python-semantic-release/commit/4f53e351960a6b658f50265384c9e8f678718f68)) * ci: move beautification to separate workflow @@ -2304,7 +2376,7 @@ Run isort and Black on pushes to master. Any edits made are committed. isort and * docs: include README.rst in index.rst -These files were very similar so it makes sense to simply include one +These files were very similar so it makes sense to simply include one inside the other. ([`8673a9d`](https://github.com/python-semantic-release/python-semantic-release/commit/8673a9d92a9bf348bb3409e002a830741396c8ca)) * docs: rewrite README.rst ([`e049772`](https://github.com/python-semantic-release/python-semantic-release/commit/e049772cf14cdd49538cf357db467f0bf3fe9587)) @@ -2334,12 +2406,12 @@ inside the other. ([`8673a9d`](https://github.com/python-semantic-release/python * ci: fetch full history in release job -I didn't realise that actions/checkout@v2 only fetches 1 commit by +I didn't realise that actions/checkout@v2 only fetches 1 commit by default. ([`a02a9b7`](https://github.com/python-semantic-release/python-semantic-release/commit/a02a9b7e34d8e7f8bb3b9c8aa1b5e1ef8bdd406c)) * ci: run tests on pull_request -The tests didn't run for #211 which caused a flake8 failure to be +The tests didn't run for #211 which caused a flake8 failure to be missed. ([`32fd77e`](https://github.com/python-semantic-release/python-semantic-release/commit/32fd77ed835bcfc943abeacec4e327df045b2ec9)) * ci: run tests on GitHub Actions ([`39ff283`](https://github.com/python-semantic-release/python-semantic-release/commit/39ff283312a0c686bfc5be71e1da9b6456652d95)) @@ -2362,7 +2434,7 @@ Automatically create pages in the API docs section using sphinx-autodoc. This is * style: fix styling from 2997908 -These code style problems were introduced because tests didn't run on +These code style problems were introduced because tests didn't run on #211. ([`172391e`](https://github.com/python-semantic-release/python-semantic-release/commit/172391ec5b5e490081b9b0ea58a94dfd5be33937)) @@ -2391,31 +2463,31 @@ These code style problems were introduced because tests didn't run on * fix: Bump dependencies and fix Windows issues on Development (#173) -* Bump dependencies and fix windows issues - -* Correctly pass temp dir to test settings - -* Remove print call on test settings - -* chore: remove py34 and py35 classifiers - -* chore: bump twine, requests and python-gitlab - -* chore: update tox config to be more granular - -* fix: missing mime types on Windows - -* chore: bump circleCI and tox python to 3.8 - -* chore: remove py36 from tox envlist - +* Bump dependencies and fix windows issues + +* Correctly pass temp dir to test settings + +* Remove print call on test settings + +* chore: remove py34 and py35 classifiers + +* chore: bump twine, requests and python-gitlab + +* chore: update tox config to be more granular + +* fix: missing mime types on Windows + +* chore: bump circleCI and tox python to 3.8 + +* chore: remove py36 from tox envlist + * chore: isort errors ([`0a6f8c3`](https://github.com/python-semantic-release/python-semantic-release/commit/0a6f8c3842b05f5f424dad5ce1fa5e3823c7e688)) ### Refactor * refactor(history): use a named tuple for parsed commits -This improves readability as we can use attributes such as 'bump' and +This improves readability as we can use attributes such as 'bump' and 'descriptions' instead of confusing numeric indices. ([`bff40d5`](https://github.com/python-semantic-release/python-semantic-release/commit/bff40d53174ffe27451d82132c31b112c7bee9fd)) @@ -2454,10 +2526,10 @@ will get the version they specify. ([`123984d`](https://github.com/python-semant * feat(build): allow config setting for build command (#195) -* feat(build): allow config setting for build command - -BREAKING CHANGE: Previously the build_commands configuration variable set the types of bundles sent to `python setup.py`. It has been replaced by the configuration variable `build_command` which takes the full command e.g. `python setup.py sdist` or `poetry build`. - +* feat(build): allow config setting for build command + +BREAKING CHANGE: Previously the build_commands configuration variable set the types of bundles sent to `python setup.py`. It has been replaced by the configuration variable `build_command` which takes the full command e.g. `python setup.py sdist` or `poetry build`. + Closes #188 ([`740f4bd`](https://github.com/python-semantic-release/python-semantic-release/commit/740f4bdb26569362acfc80f7e862fc2c750a46dd)) ### Fix @@ -2555,46 +2627,46 @@ Fixes #181 ([`0fddbe2`](https://github.com/python-semantic-release/python-semant * feat: Upload distribution files to GitHub Releases (#177) -* refactor(github): create upload_asset function - -Create a function to call the asset upload API. This will soon be used -to upload assets specified by the user. - -* refactor(github): infer Content-Type from file extension - -Infer the Content-Type header based on the file extension instead of setting it manually. - -* refactor(pypi): move building of dists to cli.py - -Refactor to have the building/removal of distributions in cli.py instead -of within the upload_to_pypi function. This makes way for uploading to -other locations, such as GitHub Releases, too. - -* feat(github): upload dists to release - -Upload Python wheels to the GitHub release. Configured with the option -upload_to_release, on by default if using GitHub. - -* docs: document upload_to_release config option - -* test(github): add tests for Github.upload_dists - -* fix(github): fix upload of .whl files - -Fix uploading of .whl files due to a missing MIME type (defined custom type as application/x-wheel+zip). Additionally, continue with other uploads even if one fails. - -* refactor(cli): additional output during publish - -Add some additional output during the publish command. - -* refactor(github): move api calls to separate methods - -Move each type of GitHub API request into its own method to improve readability. - -Re-implementation of #172 - -* fix: post changelog after PyPI upload - +* refactor(github): create upload_asset function + +Create a function to call the asset upload API. This will soon be used +to upload assets specified by the user. + +* refactor(github): infer Content-Type from file extension + +Infer the Content-Type header based on the file extension instead of setting it manually. + +* refactor(pypi): move building of dists to cli.py + +Refactor to have the building/removal of distributions in cli.py instead +of within the upload_to_pypi function. This makes way for uploading to +other locations, such as GitHub Releases, too. + +* feat(github): upload dists to release + +Upload Python wheels to the GitHub release. Configured with the option +upload_to_release, on by default if using GitHub. + +* docs: document upload_to_release config option + +* test(github): add tests for Github.upload_dists + +* fix(github): fix upload of .whl files + +Fix uploading of .whl files due to a missing MIME type (defined custom type as application/x-wheel+zip). Additionally, continue with other uploads even if one fails. + +* refactor(cli): additional output during publish + +Add some additional output during the publish command. + +* refactor(github): move api calls to separate methods + +Move each type of GitHub API request into its own method to improve readability. + +Re-implementation of #172 + +* fix: post changelog after PyPI upload + Post the changelog in-between uploading to PyPI and uploading to GitHub Releases. This is so that if the PyPI upload fails, GitHub users will not be notified. GitHub uploads still need to be processed after creating the changelog as the release notes must be published to upload assets to them. ([`e427658`](https://github.com/python-semantic-release/python-semantic-release/commit/e427658e33abf518191498c3142a0f18d3150e07)) ### Fix @@ -2614,7 +2686,7 @@ application/octet-stream is more generic, but it is better than using a non-offi * feat(history): capitalize changelog messages -Capitalize the first letter of messages in the changelog regardless of +Capitalize the first letter of messages in the changelog regardless of whether they are capitalized in the commit itself. ([`1a8e306`](https://github.com/python-semantic-release/python-semantic-release/commit/1a8e3060b8f6d6362c27903dcfc69d17db5f1d36)) ### Fix @@ -2652,7 +2724,7 @@ Fix the syntax of a broken bullet-point list in README.rst. ([`7aa572b`](https:/ * fix(github): send token in request header -Use an Authorization header instead of deprecated query parameter +Use an Authorization header instead of deprecated query parameter authorization. Fixes relekang/python-semantic-release#167 ([`be9972a`](https://github.com/python-semantic-release/python-semantic-release/commit/be9972a7b1fb183f738fb31bd370adb30281e4d5)) @@ -2723,13 +2795,13 @@ Fixes #156 ([`a4f8a10`](https://github.com/python-semantic-release/python-semant * fix: fallback to whole log if correct tag is not available (#157) -The method getting all commits to consider for the release will now test -whether the version in input is a valid reference. If it is not, it will -consider the whole log for the repository. - -evaluate_version_bump will still consider a message starting with the -version number as a breaking condition to stop analyzing. - +The method getting all commits to consider for the release will now test +whether the version in input is a valid reference. If it is not, it will +consider the whole log for the repository. + +evaluate_version_bump will still consider a message starting with the +version number as a breaking condition to stop analyzing. + Fixes #51 ([`252bffd`](https://github.com/python-semantic-release/python-semantic-release/commit/252bffd3be7b6dfcfdb384d24cb1cd83d990fc9a)) @@ -2739,14 +2811,14 @@ Fixes #51 ([`252bffd`](https://github.com/python-semantic-release/python-semanti * fix: Set version of click to >=2.0,<8.0. (#155) -* fix: Upgrade to click 7.0. - -Fixes #117 - -* fix: Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. - -* Upstream is at ~=7.0, so let's set the range to less than 8.0. - +* fix: Upgrade to click 7.0. + +Fixes #117 + +* fix: Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. + +* Upstream is at ~=7.0, so let's set the range to less than 8.0. + * The string template has no variables, so remove the call to .format() ([`f07c7f6`](https://github.com/python-semantic-release/python-semantic-release/commit/f07c7f653be1c018e443f071d9a196d9293e9521)) @@ -2937,9 +3009,9 @@ The commands is lacking from the documentation. ([`b6fa04d`](https://github.com/ ### Refactor -* refactor: added debug to hvcshvcs - +* refactor: added debug to hvcshvcs + module did not have any debug ([`0c6237b`](https://github.com/python-semantic-release/python-semantic-release/commit/0c6237bc01ec39608fb768925091c755d9bb25bd)) * refactor: fix import sorting ([`01e4c5d`](https://github.com/python-semantic-release/python-semantic-release/commit/01e4c5d743f2f237d2c85481118e467d4f5fde15)) @@ -2963,14 +3035,14 @@ module did not have any debug ([`0c6237b`](https://github.com/python-semantic-re * fix: Maintain version variable formatting on bump (#103) -Small change to the way the version is written to the config file it is read from. This allows the formatting to be the same as before semantic-release changed it. - -Prior behavior -`my_version_var="1.2.3"` => `my_version_var = '1.2.4'` - -New behavior -`my_version_var="1.2.3"` => `my_version_var="1.2.4"` - +Small change to the way the version is written to the config file it is read from. This allows the formatting to be the same as before semantic-release changed it. + +Prior behavior +`my_version_var="1.2.3"` => `my_version_var = '1.2.4'` + +New behavior +`my_version_var="1.2.3"` => `my_version_var="1.2.4"` + I am using python-semantic-release with a Julia project and this change will allow for consistent formatting in my Project.toml file where the version is maintained ([`bf63156`](https://github.com/python-semantic-release/python-semantic-release/commit/bf63156f60340614fae94c255fb2f097cf317b2b)) * fix: initialize git Repo from current folder @@ -3099,8 +3171,8 @@ Closes #88 re #32 ([`8df5e2b`](https://github.com/python-semantic-release/pytho * fix: Change requests from fixed version to version range (#93) -* Change requests version to be more flexible to aid in using this with dev requirements for a release. - +* Change requests version to be more flexible to aid in using this with dev requirements for a release. + * revert changes to vcs helpers ([`af3ad59`](https://github.com/python-semantic-release/python-semantic-release/commit/af3ad59f018876e11cc3acdda0b149f8dd5606bd)) ### Refactor @@ -3172,21 +3244,21 @@ Change the Gitpython version number to fix a bug described in #80. ([`23c9d4b`]( * feat: Add support to finding previous version from tags if not using commit messages (#68) -* feat: Be a bit more forgiving to find previous tags - -Now grabs the previous version from tag names if it can't find it in the commit - -* quantifiedcode and flake8 fixes - -* Update cli.py - +* feat: Be a bit more forgiving to find previous tags + +Now grabs the previous version from tag names if it can't find it in the commit + +* quantifiedcode and flake8 fixes + +* Update cli.py + * Switch to ImproperConfigurationError ([`6786487`](https://github.com/python-semantic-release/python-semantic-release/commit/6786487ebf4ab481139ef9f43cd74e345debb334)) * feat: Add --retry cli option (#78) -* Add --retry cli option -* Post changelog correctly -* Add comments +* Add --retry cli option +* Post changelog correctly +* Add comments * Add --retry to the docs ([`3e312c0`](https://github.com/python-semantic-release/python-semantic-release/commit/3e312c0ce79a78d25016a3b294b772983cfb5e0f)) ### Fix @@ -3222,9 +3294,9 @@ Fixes #74 ([`1dc306b`](https://github.com/python-semantic-release/python-semanti * fix: error when not in git repository (#75) -Fix an error when the program was run in a non-git repository. It would -not allow the help options to be run. - +Fix an error when the program was run in a non-git repository. It would +not allow the help options to be run. + issue #74 ([`251b190`](https://github.com/python-semantic-release/python-semantic-release/commit/251b190a2fd5df68892346926d447cbc1b32475a)) ### Unknown @@ -3278,12 +3350,12 @@ This reverts commit 93e5507da6d53ecf63405507390633ef480c52fb. ([`195ed8d`](https * feat: Add git hash to the changelog (#65) -* feat(*): add git hash to the changelog - -Add git hash to the changelog to ease finding the specific commit. The hash now is also easily viewable in Github's tag. see #63 for more information. - -* chore(test_history): fix test errors - +* feat(*): add git hash to the changelog + +Add git hash to the changelog to ease finding the specific commit. The hash now is also easily viewable in Github's tag. see #63 for more information. + +* chore(test_history): fix test errors + Fix the test errors that would happen after the modification of get_commit_log. ([`628170e`](https://github.com/python-semantic-release/python-semantic-release/commit/628170ebc440fc6abf094dd3e393f40576dedf9b)) ### Fix @@ -3997,7 +4069,7 @@ parsed. ([`974ccda`](https://github.com/python-semantic-release/python-semantic- * Merge pull request #15 from jezdez/python-2 -Add Python 2.7 support. Fix #10. +Add Python 2.7 support. Fix #10. :sparkles: ([`5daabb7`](https://github.com/python-semantic-release/python-semantic-release/commit/5daabb75eb9145566a2a7c2a9e64439df7cd85f1)) * Add Python 2.7 support. Fix #10. ([`c05e13f`](https://github.com/python-semantic-release/python-semantic-release/commit/c05e13f22163237e963c493ffeda7e140f0202c6)) diff --git a/pyproject.toml b/pyproject.toml index 6d97ddb21..297c8b2d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "8.3.0" +version = "8.4.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.7" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 04e9f9c22..78c40501b 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "8.3.0" +__version__ = "8.4.0" def setup_hook(argv: list[str]) -> None: From 07b232a3b34be0b28c6af08aea4852acb1b9bd56 Mon Sep 17 00:00:00 2001 From: bernardcooke53 <66492393+bernardcooke53@users.noreply.github.com> Date: Thu, 7 Dec 2023 20:29:03 +0000 Subject: [PATCH 009/167] feat: allow template directories to contain a '.' at the top-level (#762) --- docs/commands.rst | 2 +- semantic_release/changelog/template.py | 22 +++++++++------ semantic_release/cli/commands/version.py | 14 ++++----- tests/scenario/test_template_render.py | 36 +++++++++++++++++++++++- 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/docs/commands.rst b/docs/commands.rst index 60de6f270..1e9359b61 100644 --- a/docs/commands.rst +++ b/docs/commands.rst @@ -226,7 +226,7 @@ If ``--no-commit`` is supplied, it may disable other options derivatively; pleas ************************ Whether or not to perform a ``git tag`` to apply a tag of the corresponding to the new version during this -command invocation. This option manages the tag application separate from the commit handled by the `--commit` +command invocation. This option manages the tag application separate from the commit handled by the ``--commit`` option. If ``--no-tag`` is supplied, it may disable other options derivatively; please see below. diff --git a/semantic_release/changelog/template.py b/semantic_release/changelog/template.py index 3a2608ba7..906bfc876 100644 --- a/semantic_release/changelog/template.py +++ b/semantic_release/changelog/template.py @@ -81,22 +81,28 @@ def recursive_render( ) -> list[str]: rendered_paths: list[str] = [] for root, file in ( - (root, file) + (Path(root), file) for root, _, files in os.walk(template_dir) for file in files - if not any(elem.startswith(".") for elem in root.split(os.sep)) + # we slice relpath.parts[1:] to allow the top-level + # template folder to have a dot prefix. + # e.g. to permit ".github/psr-templates" to contain the templates, + # rather than enforcing a top-level, non-hidden directory + if not any( + elem.startswith(".") + for elem in Path(root).relative_to(template_dir).parts[1:] + ) and not file.startswith(".") ): - src_path = Path(root) - output_path = (_root_dir / src_path.relative_to(template_dir)).resolve() - log.info("Rendering templates from %s to %s", src_path, output_path) - os.makedirs(str(output_path), exist_ok=True) + output_path = (_root_dir / root.relative_to(template_dir)).resolve() + log.info("Rendering templates from %s to %s", root, output_path) + output_path.mkdir(parents=True, exist_ok=True) if file.endswith(".j2"): # We know the file ends with .j2 by the filter in the for-loop output_filename = file[:-3] # Strip off the template directory from the front of the root path - # that's the output location relative to the repo root - src_file_path = str((src_path / file).relative_to(template_dir)) + src_file_path = str((root / file).relative_to(template_dir)) output_file_path = str((output_path / output_filename).resolve()) log.debug("rendering %s to %s", src_file_path, output_file_path) @@ -107,7 +113,7 @@ def recursive_render( rendered_paths.append(output_file_path) else: - src_file = str((src_path / file).resolve()) + src_file = str((root / file).resolve()) target_file = str((output_path / file).resolve()) log.debug( "source file %s is not a template, copying to %s", src_file, target_file diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index 8be43ac29..52ce7df37 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -243,15 +243,13 @@ def version( # noqa: C901 translator.prerelease_token = prerelease_token # Only push if we're committing changes - if push_changes and not commit_changes: - if not create_tag: - log.info("changes will not be pushed because --no-commit disables pushing") - push_changes &= commit_changes + if push_changes and not commit_changes and not create_tag: + log.info("changes will not be pushed because --no-commit disables pushing") + push_changes &= commit_changes # Only push if we're creating a tag - if push_changes and not create_tag: - if not commit_changes: - log.info("new tag will not be pushed because --no-tag disables pushing") - push_changes &= create_tag + if push_changes and not create_tag and not commit_changes: + log.info("new tag will not be pushed because --no-tag disables pushing") + push_changes &= create_tag # Only make a release if we're pushing the changes if make_vcs_release and not push_changes: log.info("No vcs release will be created because pushing changes is disabled") diff --git a/tests/scenario/test_template_render.py b/tests/scenario/test_template_render.py index c4fbbb0af..86cee6739 100644 --- a/tests/scenario/test_template_render.py +++ b/tests/scenario/test_template_render.py @@ -95,7 +95,7 @@ def test_recursive_render( preexisting_paths = set(example_project.rglob("**/*")) recursive_render( - template_dir=str(tmpl_dir), + template_dir=example_project_template_dir.resolve(), environment=env, _root_dir=str(example_project.resolve()), ) @@ -128,3 +128,37 @@ def test_recursive_render( t.relative_to(example_project).parts, func=lambda *a: os.sep.join(a) ) ) + + +@pytest.fixture +def dotfolder_template_dir(example_project: Path): + newpath = example_project / ".templates/.psr-templates" + newpath.mkdir(parents=True, exist_ok=True) + return newpath + + +@pytest.fixture +def dotfolder_template(dotfolder_template_dir: Path): + tmpl = dotfolder_template_dir / "template.txt" + tmpl.write_text("I am a template") + return tmpl + + +def test_recursive_render_with_top_level_dotfolder( + example_project, dotfolder_template, dotfolder_template_dir +): + preexisting_paths = set(example_project.rglob("**/*")) + env = environment(template_dir=dotfolder_template_dir.resolve()) + + recursive_render( + template_dir=dotfolder_template_dir.resolve(), + environment=env, + _root_dir=example_project.resolve(), + ) + + rendered_template = example_project / dotfolder_template.name + assert rendered_template.exists() + + assert set(example_project.rglob("**/*")) == preexisting_paths.union( + {example_project / rendered_template} + ) From 3a571d2622de93fab7844465db9c442846c8b78d Mon Sep 17 00:00:00 2001 From: semantic-release Date: Thu, 7 Dec 2023 20:36:33 +0000 Subject: [PATCH 010/167] 8.5.0 Automatically generated by python-semantic-release --- CHANGELOG.md | 7 +++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc63c7df7..6147b6fa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ +## v8.5.0 (2023-12-07) + +### Feature + +* feat: allow template directories to contain a '.' at the top-level (#762) ([`07b232a`](https://github.com/python-semantic-release/python-semantic-release/commit/07b232a3b34be0b28c6af08aea4852acb1b9bd56)) + + ## v8.4.0 (2023-12-07) ### Build diff --git a/pyproject.toml b/pyproject.toml index 297c8b2d3..d9e745309 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "8.4.0" +version = "8.5.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.7" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 78c40501b..06d034ec3 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "8.4.0" +__version__ = "8.5.0" def setup_hook(argv: list[str]) -> None: From ea89fa72885e15da91687172355426a22c152513 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 12 Dec 2023 14:44:15 -0500 Subject: [PATCH 011/167] fix(cmd-version): handle committing of git-ignored file gracefully (#764) * fix(version): only commit non git-ignored files during version commit * test(version): set version file as ignored file Tweaks tests to use one committed change file and the version file as an ignored change file. This allows us to verify that our commit mechanism does not crash if a file that is changed is ignored by user --- semantic_release/cli/commands/version.py | 22 ++++++++++++++++++++-- tests/command_line/test_version.py | 24 ++++++++++++------------ tests/const.py | 4 ++-- tests/fixtures/example_project.py | 23 ++++++++++++++++++++--- 4 files changed, 54 insertions(+), 19 deletions(-) diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index 52ce7df37..510f3997c 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -9,6 +9,7 @@ import click import shellingham # type: ignore[import] +from git.exc import GitCommandError from semantic_release.changelog import ReleaseHistory, environment, recursive_render from semantic_release.changelog.context import make_changelog_context @@ -369,7 +370,15 @@ def version( # noqa: C901 ) elif commit_changes: - repo.git.add(all_paths_to_add) + # TODO: in future this loop should be 1 line: + # repo.index.add(all_paths_to_add, force=False) + # but since 'force' is deliberally ineffective (as in docstring) in gitpython 3.1.18 + # we have to do manually add each filepath, and catch the exception if it is an ignored file + for updated_path in all_paths_to_add: + try: + repo.git.add(updated_path) + except GitCommandError: + log.warning("Failed to add path (%s) to index", updated_path) rh = ReleaseHistory.from_git_history( repo=repo, @@ -435,7 +444,16 @@ def version( # noqa: C901 ) elif commit_changes: # Anything changed here should be staged. - repo.git.add(updated_paths) + # TODO: in future this loop should be 1 line: + # repo.index.add(updated_paths, force=False) + # but since 'force' is deliberally ineffective (as in docstring) in gitpython 3.1.18 + # we have to do manually add each filepath, and catch the exception if it is an ignored file + for updated_path in updated_paths: + try: + repo.git.add(updated_path) + except GitCommandError: + log.warning("Failed to add path (%s) to index", updated_path) + def custom_git_environment() -> ContextManager[None]: """ diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 2b39636b2..2bc6bdeda 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -389,7 +389,7 @@ def test_version_no_push_force_level( expected_new_version, example_project, example_pyproject_toml, - tmp_path_factory, + tmp_path_factory: pytest.TempPathFactory, cli_runner, ): tempdir = tmp_path_factory.mktemp("test_version") @@ -415,7 +415,7 @@ def test_version_no_push_force_level( # Changelog already reflects changes this should introduce assert differing_files == [ "pyproject.toml", - f"src/{EXAMPLE_PROJECT_NAME}/__init__.py", + f"src/{EXAMPLE_PROJECT_NAME}/_version.py", ] # Compare pyproject.toml @@ -434,14 +434,14 @@ def test_version_no_push_force_level( assert old_pyproject_toml == new_pyproject_toml assert new_version == expected_new_version - # Compare __init__.py + # Compare _version.py new_init_py = ( - (example_project / "src" / EXAMPLE_PROJECT_NAME / "__init__.py") + (example_project / "src" / EXAMPLE_PROJECT_NAME / "_version.py") .read_text(encoding="utf-8") .splitlines(keepends=True) ) old_init_py = ( - (tempdir / "src" / EXAMPLE_PROJECT_NAME / "__init__.py") + (tempdir / "src" / EXAMPLE_PROJECT_NAME / "_version.py") .read_text(encoding="utf-8") .splitlines(keepends=True) ) @@ -662,7 +662,7 @@ def test_version_only_update_files_no_git_actions( # Files that should receive version change assert differing_files == [ "pyproject.toml", - f"src/{EXAMPLE_PROJECT_NAME}/__init__.py", + f"src/{EXAMPLE_PROJECT_NAME}/_version.py", ] # Compare pyproject.toml @@ -681,20 +681,20 @@ def test_version_only_update_files_no_git_actions( assert old_pyproject_toml == new_pyproject_toml assert new_version == expected_new_version - # Compare __init__.py - new_init_py = ( - (example_project / "src" / EXAMPLE_PROJECT_NAME / "__init__.py") + # Compare _version.py + new_version_py = ( + (example_project / "src" / EXAMPLE_PROJECT_NAME / "_version.py") .read_text(encoding="utf-8") .splitlines(keepends=True) ) - old_init_py = ( - (tempdir / "src" / EXAMPLE_PROJECT_NAME / "__init__.py") + old_version_py = ( + (tempdir / "src" / EXAMPLE_PROJECT_NAME / "_version.py") .read_text(encoding="utf-8") .splitlines(keepends=True) ) d = difflib.Differ() - diff = list(d.compare(old_init_py, new_init_py)) + diff = list(d.compare(old_version_py, new_version_py)) added = [line[2:] for line in diff if line.startswith("+ ")] removed = [line[2:] for line in diff if line.startswith("- ")] diff --git a/tests/const.py b/tests/const.py index 1dcaa0756..90a7c6dec 100644 --- a/tests/const.py +++ b/tests/const.py @@ -180,7 +180,7 @@ [tool.semantic_release] version_variables = [ - "src/{EXAMPLE_PROJECT_NAME}/__init__.py:__version__", + "src/{EXAMPLE_PROJECT_NAME}/_version.py:__version__", ] version_toml = ["pyproject.toml:tool.poetry.version"] build_command = "bash -c \"echo 'Hello World'\"" @@ -375,7 +375,7 @@ def _read_long_description(): return None -with open("{EXAMPLE_PROJECT_NAME}/__init__.py", "r") as fd: +with open("{EXAMPLE_PROJECT_NAME}/_version.py", "r") as fd: version = re.search( r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE ).group(1) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 0309b9859..c0ec12c0a 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -26,7 +26,7 @@ def cd(path: Path) -> Generator[Path, None, None]: @pytest.fixture -def example_project(tmp_path): +def example_project(tmp_path: "Path") -> "Generator[Path, None, None]": with cd(tmp_path): src_dir = tmp_path / "src" src_dir.mkdir() @@ -35,11 +35,11 @@ def example_project(tmp_path): init_py = example_dir / "__init__.py" init_py.write_text( dedent( - f''' + ''' """ An example package with a very informative docstring """ - __version__ = "{EXAMPLE_PROJECT_VERSION}" + from ._version import __version__ def hello_world() -> None: @@ -47,6 +47,23 @@ def hello_world() -> None: ''' ) ) + version_py = example_dir / "_version.py" + version_py.write_text( + dedent( + f''' + __version__ = "{EXAMPLE_PROJECT_VERSION}" + ''' + ) + ) + gitignore = tmp_path / ".gitignore" + gitignore.write_text( + dedent( + f""" + *.pyc + /src/**/{version_py.name} + """ + ) + ) pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text(EXAMPLE_PYPROJECT_TOML_CONTENT) setup_cfg = tmp_path / "setup.cfg" From ac4f9aacb72c99f2479ae33369822faad011a824 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 12 Dec 2023 14:45:28 -0500 Subject: [PATCH 012/167] fix(config): gracefully fail when repo is in a detached HEAD state (#765) * fix(config): cleanly handle repository in detached HEAD state * test(cli-main): add detached head cli test --- semantic_release/cli/config.py | 12 +++++++++++- tests/command_line/test_main.py | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/semantic_release/cli/config.py b/semantic_release/cli/config.py index 9fb34c8d3..c8770691f 100644 --- a/semantic_release/cli/config.py +++ b/semantic_release/cli/config.py @@ -257,8 +257,18 @@ def from_raw_config( ## # credentials masking for logging masker = MaskingFilter(_use_named_masks=raw.logging_use_named_masks) + + try: + active_branch = repo.active_branch.name + except TypeError as err: + raise NotAReleaseBranch( + "Detached HEAD state cannot match any release groups; " + "no release will be made" + ) from err + # branch-specific configuration - branch_config = cls.select_branch_options(raw.branches, repo.active_branch.name) + branch_config = cls.select_branch_options(raw.branches, active_branch) + # commit_parser commit_parser_cls = ( _known_commit_parsers[raw.commit_parser] diff --git a/tests/command_line/test_main.py b/tests/command_line/test_main.py index dc7a97774..671a3637f 100644 --- a/tests/command_line/test_main.py +++ b/tests/command_line/test_main.py @@ -37,6 +37,20 @@ def test_not_a_release_branch_exit_code_with_strict( assert result.exit_code != 0 +def test_not_a_release_branch_detached_head_exit_code( + repo_with_git_flow_angular_commits, cli_runner +): + expected_err_msg = "Detached HEAD state cannot match any release groups; no release will be made" + + # cause repo to be in detached head state without file changes + repo_with_git_flow_angular_commits.git.checkout("HEAD", "--detach") + result = cli_runner.invoke(main, ["version", "--no-commit"]) + + # as non-strict, this will return success exit code + assert result.exit_code == 0 + assert expected_err_msg in result.stderr + + @pytest.fixture def toml_file_with_no_configuration_for_psr(tmp_path): path = tmp_path / "config.toml" From 6b2fc8c156e122ee1b43fdb513b2dc3b8fd76724 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 12 Dec 2023 14:46:37 -0500 Subject: [PATCH 013/167] docs(configuration): adjust wording and improve clarity (#766) * docs(configuration): fix typo in text * docs(configuration): adjust wording and improve clarity --- docs/configuration.rst | 78 ++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 1ffe68ebb..a3c136d93 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -4,9 +4,9 @@ Configuration ============= Configuration is read from a file which can be specified using the -:ref:`--config ` option to :ref:`cmd-main`. Python Semantic -Release currently supports either TOML- or JSON-formatted configuration, and will -attempt to detect the configuration format and parse it. +:ref:`\\\\-\\\\-config ` option to :ref:`cmd-main`. Python Semantic +Release currently supports a configuration in either TOML or JSON format, and will +attempt to auto-detect and parse either format. When using a JSON-format configuration file, Python Semantic Release looks for its settings beneath a top-level ``semantic_release`` key; when using a TOML-format @@ -50,37 +50,33 @@ the relevant settings. Environment Variables --------------------- -Some settings can be configured via environment variables. In order to do this, -you must indicate that Python Semantic Release should use a particular environment -variable as follows. - -Suppose for example that you would like to set :ref:`remote.token `. -It is possible to do so by pasting your token in plaintext into your -configuration file (**Note: this is not advisable**): +Some settings are best pulled from environment variables rather than being stored +in plaintext in your configuration file. Python Semantic Release can be configured +to look for an environment variable value to use for a given setting, but this feature +is not available for all settings. In order to use an environment variable for a setting, +you must indicate in your configuration file the name of the environment variable to use. + +The traditional and most common use case for environment variable use is for passing +authentication tokens to Python Semantic Release. You do **NOT** want to hard code your +authentication token in your configuration file, as this is a **security risk**. A plaintext +token in your configuration file could be exposed to anyone with access to your repository, +including long after its deleted if a token is in your git history. Instead, define the name +of the environment variable which contains your :ref:`remote.token `, +such as ``GH_TOKEN``, in your configuration file, and Python Semantic Release will do the +rest, as seen below. .. code-block:: toml - [tool.semantic_release.remote] - token = "very secret 123" + [tool.semantic_release.remote.token] + env = "GH_TOKEN" -Unfortunately, this configuration lives in your Git repository along with your source -code, and this would represent insecure management of your password. It is recommended -to use an environment variable to provide the required password. Suppose you would -like to specify that should be read from the environment variable ``GH_TOKEN``. -In this case, you should modify your configuration to the following: +Given basic TOML syntax compatibility, this is equivalent to: .. code-block:: toml [tool.semantic_release.remote] token = { env = "GH_TOKEN" } -This is equivalent to the default: - -.. code-block:: toml - - [tool.semantic_release.remote.token] - env = "GH_TOKEN" - The general format for specifying that some configuration should be sourced from an environment variable is: @@ -187,13 +183,14 @@ adding the old message pattern(s) to :ref:`exclude_commit_patterns `, ``"emoji"`` for -:ref:`EmojiCommitParser `, ``"scipy"`` for -:ref:`` or ``"tag"`` for -:ref:`TagCommitParser `. However you can also specify your own -commit parser in ``module:attr`` form, in which case this will be imported and used -instead. +Built-in parsers: + * ``angular`` - :ref:`AngularCommitParser ` + * ``emoji`` - :ref:`EmojiCommitParser ` + * ``scipy`` - :ref:`ScipyCommitParser ` + * ``tag`` - :ref:`TagCommitParser ` + +You can set any of the built-in parsers by their keyword but you can also specify +your own commit parser in ``module:attr`` form. For more information see :ref:`commit-parsing`. @@ -375,7 +372,7 @@ The patterns in this list are treated as regular expressions. ************************************************* .. note:: - This section of the configuration contains options which customise the template + This section of the configuration contains options which customize the template environment used to render templates such as the changelog. Most options are passed directly to the `jinja2.Environment`_ constructor, and further documentation one these parameters can be found there. @@ -559,12 +556,13 @@ pushing. .. _config-remote-token: -``token`` (:ref:`Environment Variable `) -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +``token (Dict['env': str])`` +"""""""""""""""""""""""""""" -Environment variable from which to source the authentication token for the remote VCS. -Common examples include ``"GH_TOKEN"``, ``"GITLAB_TOKEN"`` or ``"GITEA_TOKEN"``, however -you can choose to use a custom environment variable if you wish. +:ref:`Environment Variable ` from which to source the +authentication token for the remote VCS. Common examples include ``"GH_TOKEN"``, +``"GITLAB_TOKEN"`` or ``"GITEA_TOKEN"``, however, you may choose to use a custom +environment variable if you wish. .. note:: By default, this is a **mandatory** environment variable that must be set before @@ -603,9 +601,9 @@ list should be a string containing a Unix-style glob pattern. ``upload_to_vcs_release (bool)`` """""""""""""""""""""""""""""""" -If set to ``true``, upload artefacts matching +If set to ``true``, upload any artifacts matched by the :ref:`dist_glob_patterns ` to the release created -in the remote VCS corresponding to the latest tag, if that is supported by the -:ref:`VCS type `. +in the remote VCS corresponding to the latest tag. Artifacts are only uploaded if +release artifact uploads are supported by the :ref:`VCS type `. **Default:** ``true`` From 9bf69d7005eee75f20b356bda97fea2d250a91de Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 12 Dec 2023 19:51:34 +0000 Subject: [PATCH 014/167] style: beautify 6b2fc8c156e122ee1b43fdb513b2dc3b8fd76724 --- semantic_release/cli/commands/version.py | 1 - tests/command_line/test_main.py | 4 +++- tests/fixtures/example_project.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index 510f3997c..0039800dc 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -454,7 +454,6 @@ def version( # noqa: C901 except GitCommandError: log.warning("Failed to add path (%s) to index", updated_path) - def custom_git_environment() -> ContextManager[None]: """ git.custom_environment is a context manager but diff --git a/tests/command_line/test_main.py b/tests/command_line/test_main.py index 671a3637f..0c8d3c5f6 100644 --- a/tests/command_line/test_main.py +++ b/tests/command_line/test_main.py @@ -40,7 +40,9 @@ def test_not_a_release_branch_exit_code_with_strict( def test_not_a_release_branch_detached_head_exit_code( repo_with_git_flow_angular_commits, cli_runner ): - expected_err_msg = "Detached HEAD state cannot match any release groups; no release will be made" + expected_err_msg = ( + "Detached HEAD state cannot match any release groups; no release will be made" + ) # cause repo to be in detached head state without file changes repo_with_git_flow_angular_commits.git.checkout("HEAD", "--detach") diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index c0ec12c0a..332059003 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -50,9 +50,9 @@ def hello_world() -> None: version_py = example_dir / "_version.py" version_py.write_text( dedent( - f''' + f""" __version__ = "{EXAMPLE_PROJECT_VERSION}" - ''' + """ ) ) gitignore = tmp_path / ".gitignore" From 0400d7b4f30a5fc1638fd704679871012afb144e Mon Sep 17 00:00:00 2001 From: semantic-release Date: Tue, 12 Dec 2023 19:53:38 +0000 Subject: [PATCH 015/167] 8.5.1 Automatically generated by python-semantic-release --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6147b6fa4..c25e2d861 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ +## v8.5.1 (2023-12-12) + +### Documentation + +* docs(configuration): adjust wording and improve clarity (#766) + +* docs(configuration): fix typo in text + +* docs(configuration): adjust wording and improve clarity ([`6b2fc8c`](https://github.com/python-semantic-release/python-semantic-release/commit/6b2fc8c156e122ee1b43fdb513b2dc3b8fd76724)) + +### Fix + +* fix(config): gracefully fail when repo is in a detached HEAD state (#765) + +* fix(config): cleanly handle repository in detached HEAD state + +* test(cli-main): add detached head cli test ([`ac4f9aa`](https://github.com/python-semantic-release/python-semantic-release/commit/ac4f9aacb72c99f2479ae33369822faad011a824)) + +* fix(cmd-version): handle committing of git-ignored file gracefully (#764) + +* fix(version): only commit non git-ignored files during version commit + +* test(version): set version file as ignored file + +Tweaks tests to use one committed change file and the version file +as an ignored change file. This allows us to verify that our commit +mechanism does not crash if a file that is changed is ignored by user ([`ea89fa7`](https://github.com/python-semantic-release/python-semantic-release/commit/ea89fa72885e15da91687172355426a22c152513)) + +### Style + +* style: beautify 6b2fc8c156e122ee1b43fdb513b2dc3b8fd76724 ([`9bf69d7`](https://github.com/python-semantic-release/python-semantic-release/commit/9bf69d7005eee75f20b356bda97fea2d250a91de)) + + ## v8.5.0 (2023-12-07) ### Feature diff --git a/pyproject.toml b/pyproject.toml index d9e745309..9ad4b7aad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "8.5.0" +version = "8.5.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.7" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 06d034ec3..f5363a6c8 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "8.5.0" +__version__ = "8.5.1" def setup_hook(argv: list[str]) -> None: From c48c3b370335931d63391d1a4f5802937deff178 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Dec 2023 19:57:59 +0000 Subject: [PATCH 016/167] build(deps-dev): bump ruff from 0.1.6 to 0.1.7 (#769) * build(deps-dev): bump ruff from 0.1.6 to 0.1.7 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.6 to 0.1.7. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/v0.1.6...v0.1.7) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * ci: remove hardcoded ruff version in workflows --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bernard Cooke --- .github/workflows/main.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9f20641e5..a83d152ca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -83,7 +83,7 @@ jobs: uses: actions/checkout@v3 - name: Install Ruff - run: python -m pip install ruff==0.1.2 + run: python -m pip install ".[dev]" - name: Format run: | diff --git a/pyproject.toml b/pyproject.toml index 9ad4b7aad..2bf69cd02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,7 @@ test = [ "requests-mock>=1.10.0,<2", "types-pytest-lazy-fixture>=0.6.3.3", ] -dev = ["pre-commit", "tox", "ruff==0.1.6"] +dev = ["pre-commit", "tox", "ruff==0.1.7"] mypy = ["mypy", "types-requests"] [tool.pytest.ini_options] From bb3b63111d0e02bd53c2ed25d5ab0e5a3d532136 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 12 Dec 2023 20:03:05 +0000 Subject: [PATCH 017/167] style: beautify c48c3b370335931d63391d1a4f5802937deff178 --- semantic_release/hvcs/util.py | 2 +- semantic_release/version/version.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/semantic_release/hvcs/util.py b/semantic_release/hvcs/util.py index c42f7a74f..fd0d66ebc 100644 --- a/semantic_release/hvcs/util.py +++ b/semantic_release/hvcs/util.py @@ -64,7 +64,7 @@ def suppress_http_error_for_codes( """ def _suppress_http_error_for_codes( - func: Callable[..., _R] + func: Callable[..., _R], ) -> Callable[..., _R | None]: @wraps(func) def _wrapper(*a: Any, **kw: Any) -> _R | None: diff --git a/semantic_release/version/version.py b/semantic_release/version/version.py index 529e70cc0..de0b879a8 100644 --- a/semantic_release/version/version.py +++ b/semantic_release/version/version.py @@ -23,7 +23,8 @@ @overload def _comparator( - *, type_guard: bool + *, + type_guard: bool, ) -> Callable[[VersionComparator], VersionComparator]: ... From e8c9d516c37466a5dce75a73766d5be0f9e74627 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 19 Dec 2023 12:53:56 -0500 Subject: [PATCH 018/167] fix(cli): gracefully output configuration validation errors (#772) * test(fixtures): update example project workflow & add config modifier * test(cli-main): add test for raw config validation error * fix(cli): gracefully output configuration validation errors --- semantic_release/cli/commands/main.py | 8 +- tests/command_line/test_main.py | 19 ++++ tests/fixtures/example_project.py | 144 ++++++++++++++++---------- 3 files changed, 115 insertions(+), 56 deletions(-) diff --git a/semantic_release/cli/commands/main.py b/semantic_release/cli/commands/main.py index 65c0af552..13d1cc13f 100644 --- a/semantic_release/cli/commands/main.py +++ b/semantic_release/cli/commands/main.py @@ -7,6 +7,7 @@ from click.core import ParameterSource from git import InvalidGitRepositoryError from git.repo.base import Repo +from pydantic import ValidationError from rich.console import Console from rich.logging import RichHandler @@ -142,8 +143,8 @@ def main( except InvalidConfiguration as exc: ctx.fail(str(exc)) - raw_config = RawConfig.model_validate(config_text) try: + raw_config = RawConfig.model_validate(config_text) runtime = RuntimeContext.from_raw_config( raw_config, repo=repo, global_cli_options=cli_options ) @@ -153,8 +154,9 @@ def main( # multibranch CI it might be desirable to run a non-release branch's pipeline # without specifying conditional execution of PSR based on branch name ctx.exit(2 if strict else 0) - except InvalidConfiguration as exc: - ctx.fail(str(exc)) + except (ValidationError, InvalidConfiguration) as exc: + click.echo(str(exc), err=True) + ctx.exit(1) ctx.obj = runtime # This allows us to mask secrets in the logging diff --git a/tests/command_line/test_main.py b/tests/command_line/test_main.py index 0c8d3c5f6..625fd4b7d 100644 --- a/tests/command_line/test_main.py +++ b/tests/command_line/test_main.py @@ -1,6 +1,7 @@ import json import os from textwrap import dedent +from typing import TYPE_CHECKING import git import pytest @@ -8,6 +9,11 @@ from semantic_release import __version__ from semantic_release.cli import main +if TYPE_CHECKING: + from click.testing import CliRunner + + from tests.fixtures.example_project import UpdatePyprojectTomlFn + def test_main_prints_version_and_exits(cli_runner): result = cli_runner.invoke(main, ["--version"]) @@ -116,6 +122,19 @@ def test_errors_when_config_file_does_not_exist_and_passed_explicitly( assert "does not exist" in result.stderr +@pytest.mark.usefixtures("repo_with_no_tags_angular_commits") +def test_errors_when_config_file_invalid_configuration( + cli_runner: "CliRunner", update_pyproject_toml: "UpdatePyprojectTomlFn" +): + update_pyproject_toml("tool.semantic_release.remote.type", "invalidType") + result = cli_runner.invoke(main, ["--config", "pyproject.toml", "version"]) + + stderr_lines = result.stderr.splitlines() + assert result.exit_code == 1 + assert "1 validation error for RawConfig" in stderr_lines[0] + assert "remote.type" in stderr_lines[1] + + def test_uses_default_config_when_no_config_file_found( tmp_path, cli_runner, diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 332059003..891d0fc1b 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -1,10 +1,10 @@ import os -from contextlib import contextmanager from pathlib import Path from textwrap import dedent -from typing import Generator +from typing import TYPE_CHECKING, Generator import pytest +import tomlkit from tests.const import ( EXAMPLE_CHANGELOG_MD_CONTENT, @@ -16,65 +16,76 @@ EXAMPLE_SETUP_PY_CONTENT, ) +if TYPE_CHECKING: + from typing import Any, Protocol -@contextmanager -def cd(path: Path) -> Generator[Path, None, None]: + ExProjectDir = Path + + class UpdatePyprojectTomlFn(Protocol): + def __call__(self, setting: str, value: "Any") -> None: + ... + + +@pytest.fixture +def change_to_tmp_dir(tmp_path: "Path") -> "Generator[None, None, None]": cwd = os.getcwd() - os.chdir(str(path.resolve())) - yield path - os.chdir(cwd) + os.chdir(str(tmp_path.resolve())) + try: + yield + finally: + os.chdir(cwd) @pytest.fixture -def example_project(tmp_path: "Path") -> "Generator[Path, None, None]": - with cd(tmp_path): - src_dir = tmp_path / "src" - src_dir.mkdir() - example_dir = src_dir / EXAMPLE_PROJECT_NAME - example_dir.mkdir() - init_py = example_dir / "__init__.py" - init_py.write_text( - dedent( - ''' - """ - An example package with a very informative docstring - """ - from ._version import __version__ - - - def hello_world() -> None: - print("Hello World") - ''' - ) +def example_project(change_to_tmp_dir: None) -> "ExProjectDir": + tmp_path = Path.cwd() + src_dir = tmp_path / "src" + src_dir.mkdir() + example_dir = src_dir / EXAMPLE_PROJECT_NAME + example_dir.mkdir() + init_py = example_dir / "__init__.py" + init_py.write_text( + dedent( + ''' + """ + An example package with a very informative docstring + """ + from ._version import __version__ + + + def hello_world() -> None: + print("Hello World") + ''' ) - version_py = example_dir / "_version.py" - version_py.write_text( - dedent( - f""" - __version__ = "{EXAMPLE_PROJECT_VERSION}" - """ - ) + ) + version_py = example_dir / "_version.py" + version_py.write_text( + dedent( + f""" + __version__ = "{EXAMPLE_PROJECT_VERSION}" + """ ) - gitignore = tmp_path / ".gitignore" - gitignore.write_text( - dedent( - f""" - *.pyc - /src/**/{version_py.name} - """ - ) + ) + gitignore = tmp_path / ".gitignore" + gitignore.write_text( + dedent( + f""" + *.pyc + /src/**/{version_py.name} + """ ) - pyproject_toml = tmp_path / "pyproject.toml" - pyproject_toml.write_text(EXAMPLE_PYPROJECT_TOML_CONTENT) - setup_cfg = tmp_path / "setup.cfg" - setup_cfg.write_text(EXAMPLE_SETUP_CFG_CONTENT) - setup_py = tmp_path / "setup.py" - setup_py.write_text(EXAMPLE_SETUP_PY_CONTENT) - template_dir = tmp_path / "templates" - template_dir.mkdir() - changelog_md = tmp_path / "CHANGELOG.md" - changelog_md.write_text(EXAMPLE_CHANGELOG_MD_CONTENT) - yield tmp_path + ) + pyproject_toml = tmp_path / "pyproject.toml" + pyproject_toml.write_text(EXAMPLE_PYPROJECT_TOML_CONTENT) + setup_cfg = tmp_path / "setup.cfg" + setup_cfg.write_text(EXAMPLE_SETUP_CFG_CONTENT) + setup_py = tmp_path / "setup.py" + setup_py.write_text(EXAMPLE_SETUP_PY_CONTENT) + template_dir = tmp_path / "templates" + template_dir.mkdir() + changelog_md = tmp_path / "CHANGELOG.md" + changelog_md.write_text(EXAMPLE_CHANGELOG_MD_CONTENT) + return tmp_path @pytest.fixture @@ -109,3 +120,30 @@ def example_changelog_md(example_project): @pytest.fixture def example_project_template_dir(example_project): return example_project / "templates" + + +@pytest.fixture +def update_pyproject_toml( + example_project: "Path", example_pyproject_toml: "Path" +) -> "UpdatePyprojectTomlFn": + """Update the pyproject.toml file with the given content.""" + def _update_pyproject_toml(setting: str, value: "Any") -> None: + with open(example_pyproject_toml) as rfd: + pyproject_toml = tomlkit.load(rfd) + + new_setting = {} + parts = setting.split(".") + new_setting_key = parts.pop(-1) + new_setting[new_setting_key] = value + + pointer = pyproject_toml + for part in parts: + if pointer.get(part, None) is None: + pointer.add(part, tomlkit.table()) + pointer = pointer.get(part, {}) + pointer.update(new_setting) + + with open(example_pyproject_toml, 'w') as wfd: + tomlkit.dump(pyproject_toml, wfd) + + return _update_pyproject_toml From 5efda8acfed938d3188cd55678ace20ecac7f798 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:54:21 +0000 Subject: [PATCH 019/167] build(deps-dev): bump ruff from 0.1.7 to 0.1.8 (#775) Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.7 to 0.1.8. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/v0.1.7...v0.1.8) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2bf69cd02..42ac2c352 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,7 @@ test = [ "requests-mock>=1.10.0,<2", "types-pytest-lazy-fixture>=0.6.3.3", ] -dev = ["pre-commit", "tox", "ruff==0.1.7"] +dev = ["pre-commit", "tox", "ruff==0.1.8"] mypy = ["mypy", "types-requests"] [tool.pytest.ini_options] From 98b10b3f08af16ab5cb00096b288afefbee1b74f Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 19 Dec 2023 17:59:41 +0000 Subject: [PATCH 020/167] style: beautify 5efda8acfed938d3188cd55678ace20ecac7f798 --- tests/fixtures/example_project.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 891d0fc1b..2a5fb47e5 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -127,6 +127,7 @@ def update_pyproject_toml( example_project: "Path", example_pyproject_toml: "Path" ) -> "UpdatePyprojectTomlFn": """Update the pyproject.toml file with the given content.""" + def _update_pyproject_toml(setting: str, value: "Any") -> None: with open(example_pyproject_toml) as rfd: pyproject_toml = tomlkit.load(rfd) @@ -143,7 +144,7 @@ def _update_pyproject_toml(setting: str, value: "Any") -> None: pointer = pointer.get(part, {}) pointer.update(new_setting) - with open(example_pyproject_toml, 'w') as wfd: + with open(example_pyproject_toml, "w") as wfd: tomlkit.dump(pyproject_toml, wfd) return _update_pyproject_toml From 8476c85a21115ac2dc3a5299d151513a662b53ff Mon Sep 17 00:00:00 2001 From: semantic-release Date: Tue, 19 Dec 2023 18:01:31 +0000 Subject: [PATCH 021/167] 8.5.2 Automatically generated by python-semantic-release --- CHANGELOG.md | 64 ++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c25e2d861..c2a720c53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,70 @@ +## v8.5.2 (2023-12-19) + +### Build + +* build(deps-dev): bump ruff from 0.1.7 to 0.1.8 (#775) + +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.7 to 0.1.8. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.7...v0.1.8) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> +Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`5efda8a`](https://github.com/python-semantic-release/python-semantic-release/commit/5efda8acfed938d3188cd55678ace20ecac7f798)) + +* build(deps-dev): bump ruff from 0.1.6 to 0.1.7 (#769) + +* build(deps-dev): bump ruff from 0.1.6 to 0.1.7 + +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.6 to 0.1.7. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.6...v0.1.7) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> + +* ci: remove hardcoded ruff version in workflows + +--------- + +Signed-off-by: dependabot[bot] <support@github.com> +Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> +Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> ([`c48c3b3`](https://github.com/python-semantic-release/python-semantic-release/commit/c48c3b370335931d63391d1a4f5802937deff178)) + +### Fix + +* fix(cli): gracefully output configuration validation errors (#772) + +* test(fixtures): update example project workflow & add config modifier + +* test(cli-main): add test for raw config validation error + +* fix(cli): gracefully output configuration validation errors ([`e8c9d51`](https://github.com/python-semantic-release/python-semantic-release/commit/e8c9d516c37466a5dce75a73766d5be0f9e74627)) + +### Style + +* style: beautify 5efda8acfed938d3188cd55678ace20ecac7f798 ([`98b10b3`](https://github.com/python-semantic-release/python-semantic-release/commit/98b10b3f08af16ab5cb00096b288afefbee1b74f)) + +* style: beautify c48c3b370335931d63391d1a4f5802937deff178 ([`bb3b631`](https://github.com/python-semantic-release/python-semantic-release/commit/bb3b63111d0e02bd53c2ed25d5ab0e5a3d532136)) + + ## v8.5.1 (2023-12-12) ### Documentation diff --git a/pyproject.toml b/pyproject.toml index 42ac2c352..00f0cb94f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "8.5.1" +version = "8.5.2" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.7" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index f5363a6c8..b3340feaa 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "8.5.1" +__version__ = "8.5.2" def setup_hook(argv: list[str]) -> None: From 245e878f02d5cafec6baf0493c921c1e396b56e8 Mon Sep 17 00:00:00 2001 From: cc-stjm <47748595+cc-stjm@users.noreply.github.com> Date: Fri, 22 Dec 2023 11:30:26 +0000 Subject: [PATCH 022/167] docs: minor correction to commit-parsing documentation (#777) --- docs/commit-parsing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commit-parsing.rst b/docs/commit-parsing.rst index 10e53af39..0c6442c66 100644 --- a/docs/commit-parsing.rst +++ b/docs/commit-parsing.rst @@ -354,7 +354,7 @@ Therefore, a custom commit parser could be implemented via: class MyCommitParser( semantic_release.CommitParser[semantic_release.ParseResult, MyParserOptions] ): - def parse(self, commit: git.object.commit.Commit) -> semantic_release.ParseResult: + def parse(self, commit: git.objects.commit.Commit) -> semantic_release.ParseResult: ... .. _angular commit guidelines: https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits From cf75f237360488ebb0088e5b8aae626e97d9cbdd Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 22 Dec 2023 06:35:37 -0500 Subject: [PATCH 023/167] feat(utils): expand parsable valid git remote url formats (#771) Git remote url parsing now supports additional formats (ssh, https, file, git) --- semantic_release/helpers.py | 112 ++++++++++++-------- tests/unit/semantic_release/test_helpers.py | 82 ++++++++++++++ 2 files changed, 152 insertions(+), 42 deletions(-) create mode 100644 tests/unit/semantic_release/test_helpers.py diff --git a/semantic_release/helpers.py b/semantic_release/helpers.py index 2780ffbb0..51f78a513 100644 --- a/semantic_release/helpers.py +++ b/semantic_release/helpers.py @@ -3,6 +3,7 @@ import re import string from functools import lru_cache, wraps +from pathlib import PurePosixPath from typing import Any, Callable, NamedTuple, TypeVar from urllib.parse import urlsplit @@ -73,7 +74,7 @@ def dynamic_import(import_path: str) -> Any: class ParsedGitUrl(NamedTuple): - """Container for the elements parsed from a git URL using GIT_URL_REGEX""" + """Container for the elements parsed from a git URL""" scheme: str netloc: str @@ -81,59 +82,86 @@ class ParsedGitUrl(NamedTuple): repo_name: str -GIT_URL_REGEX = re.compile( - r""" - ^ - (?P[\w\d\-.]+)@ - (?P[^:]+) - : - (?P[\w\.@\:/\-~]+) - / - (?P[\w\.\_\-]+) # Note this also catches the ".git" at the end if present - /? - $ - """, # noqa: E501 - flags=re.VERBOSE, -) - - @lru_cache(maxsize=512) def parse_git_url(url: str) -> ParsedGitUrl: """ - Attempt to parse a string as a git url, either https or ssh format, into a + Attempt to parse a string as a git url http[s]://, git://, file://, or ssh format, into a ParsedGitUrl. + + supported examples: + http://git.mycompany.com/username/myproject.git + https://github.com/username/myproject.git + https://gitlab.com/group/subgroup/myproject.git + https://git.mycompany.com:4443/username/myproject.git + git://host.xz/path/to/repo.git/ + git://host.xz:9418/path/to/repo.git/ + git@github.com:username/myproject.git <-- assumes ssh:// + ssh://git@github.com:3759/myproject.git <-- non-standard, but assume user 3759 + ssh://git@github.com:username/myproject.git + ssh://git@bitbucket.org:7999/username/myproject.git + git+ssh://git@github.com:username/myproject.git + /Users/username/dev/remote/myproject.git <-- Posix File paths + file:///Users/username/dev/remote/myproject.git + C:/Users/username/dev/remote/myproject.git <-- Windows File paths + file:///C:/Users/username/dev/remote/myproject.git + + REFERENCE: https://stackoverflow.com/questions/31801271/what-are-the-supported-git-url-formats + Raises ValueError if the url can't be parsed. """ log.debug("Parsing git url %r", url) + + # Normalizers are a list of tuples of (pattern, replacement) + normalizers = [ + # normalize implicit ssh urls to explicit ssh:// + (r"^(\w+@)", r"ssh://\1"), + + # normalize git+ssh:// urls to ssh:// + (r"^git\+ssh://", "ssh://"), + + # normalize an scp like syntax to URL compatible syntax + # excluding port definitions (:#####) & including numeric usernames + (r"(ssh://(?:\w+@)?[\w.]+):(?!\d{1,5}/\w+/)(.*)$", r"\1/\2"), + + # normalize implicit file (windows || posix) urls to explicit file:// urls + (r"^([C-Z]:/)|^/(\w)", r"file:///\1\2"), + ] + + for pattern, replacement in normalizers: + url = re.compile(pattern).sub(replacement, url) + + # run the url through urlsplit to separate out the parts urllib_split = urlsplit(url) - if urllib_split.scheme: - # We have been able to parse the url with urlsplit, - # so it's a (git|ssh|https?)://... structure - namespace, _, name = urllib_split.path.lstrip("/").rpartition("/") - name.rstrip("/") - name = name[:-4] if name.endswith(".git") else name - if not all((urllib_split.scheme, urllib_split.netloc, namespace, name)): - raise ValueError(f"Bad url: {url!r}") - - return ParsedGitUrl( - scheme=urllib_split.scheme, - netloc=urllib_split.netloc, - namespace=namespace, - repo_name=name, - ) - m = GIT_URL_REGEX.match(url) - if not m: + # Fail if url scheme not found + if not urllib_split.scheme: raise ValueError(f"Cannot parse {url!r}") - repo_name = m.group("repo_name") - repo_name = repo_name[:-4] if repo_name.endswith(".git") else repo_name + # We have been able to parse the url with urlsplit, + # so it's a (file|git|ssh|https?)://... structure + # but we aren't validating the protocol scheme as its not our business + + # use PosixPath to normalize the path & then separate out the namespace & repo_name + namespace, _, name = str(PurePosixPath(urllib_split.path)).lstrip("/").rpartition("/") - if not all((*m.group("netloc", "namespace"), repo_name)): + # strip out the .git at the end of the repo_name if present + name = name[:-4] if name.endswith(".git") else name + + # check that we have all the required parts of the url + required_parts = [ + urllib_split.scheme, + # Allow empty net location for file:// urls + True if urllib_split.scheme == "file" else urllib_split.netloc, + namespace, + name + ] + + if not all(required_parts): raise ValueError(f"Bad url: {url!r}") + return ParsedGitUrl( - scheme="ssh", - netloc=m.group("netloc"), - namespace=m.group("namespace"), - repo_name=repo_name, + scheme=urllib_split.scheme, + netloc=urllib_split.netloc, + namespace=namespace, + repo_name=name, ) diff --git a/tests/unit/semantic_release/test_helpers.py b/tests/unit/semantic_release/test_helpers.py new file mode 100644 index 000000000..3529dc28b --- /dev/null +++ b/tests/unit/semantic_release/test_helpers.py @@ -0,0 +1,82 @@ +import pytest + +from semantic_release.helpers import ParsedGitUrl, parse_git_url + + +@pytest.mark.parametrize(('url', 'expected'), [ + ( + "http://git.mycompany.com/username/myproject.git", + ParsedGitUrl("http", "git.mycompany.com", "username", "myproject") + ), + ( + "https://github.com/username/myproject.git", + ParsedGitUrl("https", "github.com", "username", "myproject") + ), + ( + "https://gitlab.com/group/subgroup/myproject.git", + ParsedGitUrl("https", "gitlab.com", "group/subgroup", "myproject") + ), + ( + "https://git.mycompany.com:4443/username/myproject.git", + ParsedGitUrl("https", "git.mycompany.com:4443", "username", "myproject") + ), + ( + "git://host.xz/path/to/repo.git/", + ParsedGitUrl("git", "host.xz", "path/to", "repo") + ), + ( + "git://host.xz:9418/path/to/repo.git/", + ParsedGitUrl("git", "host.xz:9418", "path/to", "repo") + ), + ( + "git@github.com:username/myproject.git", + ParsedGitUrl("ssh", "git@github.com", "username", "myproject") + ), + ( + "ssh://git@github.com:3759/myproject.git", + ParsedGitUrl("ssh", "git@github.com", "3759", "myproject") + ), + ( + "ssh://git@github.com:username/myproject.git", + ParsedGitUrl("ssh", "git@github.com", "username", "myproject") + ), + ( + "ssh://git@bitbucket.org:7999/username/myproject.git", + ParsedGitUrl("ssh", "git@bitbucket.org:7999", "username", "myproject") + ), + ( + "git+ssh://git@github.com:username/myproject.git", + ParsedGitUrl("ssh", "git@github.com", "username", "myproject") + ), + ( + "/Users/username/dev/remote/myproject.git", + ParsedGitUrl("file", "", "Users/username/dev/remote", "myproject") + ), + ( + "file:///Users/username/dev/remote/myproject.git", + ParsedGitUrl("file", "", "Users/username/dev/remote", "myproject") + ), + ( + "C:/Users/username/dev/remote/myproject.git", + ParsedGitUrl("file", "", "C:/Users/username/dev/remote", "myproject") + ), + ( + "file:///C:/Users/username/dev/remote/myproject.git", + ParsedGitUrl("file", "", "C:/Users/username/dev/remote", "myproject") + ), +]) +def test_parse_valid_git_urls(url: str, expected: ParsedGitUrl): + """Test that a valid given git remote url is parsed correctly.""" + assert expected == parse_git_url(url) + + +@pytest.mark.parametrize('url', [ + "icmp://git", + "abcdefghijklmnop.git", + "../relative/path/to/repo.git", + "http://domain/project.git" +]) +def test_parse_invalid_git_urls(url: str): + """Test that an invalid git remote url throws a ValueError.""" + with pytest.raises(ValueError): + parse_git_url(url) From 2de634d6e1fed29e8ce55a1c57fd23bf838badd9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 22 Dec 2023 11:40:36 +0000 Subject: [PATCH 024/167] style: beautify cf75f237360488ebb0088e5b8aae626e97d9cbdd --- semantic_release/helpers.py | 9 +- tests/unit/semantic_release/test_helpers.py | 142 ++++++++++---------- 2 files changed, 78 insertions(+), 73 deletions(-) diff --git a/semantic_release/helpers.py b/semantic_release/helpers.py index 51f78a513..fb0070d9a 100644 --- a/semantic_release/helpers.py +++ b/semantic_release/helpers.py @@ -115,14 +115,11 @@ def parse_git_url(url: str) -> ParsedGitUrl: normalizers = [ # normalize implicit ssh urls to explicit ssh:// (r"^(\w+@)", r"ssh://\1"), - # normalize git+ssh:// urls to ssh:// (r"^git\+ssh://", "ssh://"), - # normalize an scp like syntax to URL compatible syntax # excluding port definitions (:#####) & including numeric usernames (r"(ssh://(?:\w+@)?[\w.]+):(?!\d{1,5}/\w+/)(.*)$", r"\1/\2"), - # normalize implicit file (windows || posix) urls to explicit file:// urls (r"^([C-Z]:/)|^/(\w)", r"file:///\1\2"), ] @@ -142,7 +139,9 @@ def parse_git_url(url: str) -> ParsedGitUrl: # but we aren't validating the protocol scheme as its not our business # use PosixPath to normalize the path & then separate out the namespace & repo_name - namespace, _, name = str(PurePosixPath(urllib_split.path)).lstrip("/").rpartition("/") + namespace, _, name = ( + str(PurePosixPath(urllib_split.path)).lstrip("/").rpartition("/") + ) # strip out the .git at the end of the repo_name if present name = name[:-4] if name.endswith(".git") else name @@ -153,7 +152,7 @@ def parse_git_url(url: str) -> ParsedGitUrl: # Allow empty net location for file:// urls True if urllib_split.scheme == "file" else urllib_split.netloc, namespace, - name + name, ] if not all(required_parts): diff --git a/tests/unit/semantic_release/test_helpers.py b/tests/unit/semantic_release/test_helpers.py index 3529dc28b..2a63ab2c0 100644 --- a/tests/unit/semantic_release/test_helpers.py +++ b/tests/unit/semantic_release/test_helpers.py @@ -3,79 +3,85 @@ from semantic_release.helpers import ParsedGitUrl, parse_git_url -@pytest.mark.parametrize(('url', 'expected'), [ - ( - "http://git.mycompany.com/username/myproject.git", - ParsedGitUrl("http", "git.mycompany.com", "username", "myproject") - ), - ( - "https://github.com/username/myproject.git", - ParsedGitUrl("https", "github.com", "username", "myproject") - ), - ( - "https://gitlab.com/group/subgroup/myproject.git", - ParsedGitUrl("https", "gitlab.com", "group/subgroup", "myproject") - ), - ( - "https://git.mycompany.com:4443/username/myproject.git", - ParsedGitUrl("https", "git.mycompany.com:4443", "username", "myproject") - ), - ( - "git://host.xz/path/to/repo.git/", - ParsedGitUrl("git", "host.xz", "path/to", "repo") - ), - ( - "git://host.xz:9418/path/to/repo.git/", - ParsedGitUrl("git", "host.xz:9418", "path/to", "repo") - ), - ( - "git@github.com:username/myproject.git", - ParsedGitUrl("ssh", "git@github.com", "username", "myproject") - ), - ( - "ssh://git@github.com:3759/myproject.git", - ParsedGitUrl("ssh", "git@github.com", "3759", "myproject") - ), - ( - "ssh://git@github.com:username/myproject.git", - ParsedGitUrl("ssh", "git@github.com", "username", "myproject") - ), - ( - "ssh://git@bitbucket.org:7999/username/myproject.git", - ParsedGitUrl("ssh", "git@bitbucket.org:7999", "username", "myproject") - ), - ( - "git+ssh://git@github.com:username/myproject.git", - ParsedGitUrl("ssh", "git@github.com", "username", "myproject") - ), - ( - "/Users/username/dev/remote/myproject.git", - ParsedGitUrl("file", "", "Users/username/dev/remote", "myproject") - ), - ( - "file:///Users/username/dev/remote/myproject.git", - ParsedGitUrl("file", "", "Users/username/dev/remote", "myproject") - ), - ( - "C:/Users/username/dev/remote/myproject.git", - ParsedGitUrl("file", "", "C:/Users/username/dev/remote", "myproject") - ), - ( - "file:///C:/Users/username/dev/remote/myproject.git", - ParsedGitUrl("file", "", "C:/Users/username/dev/remote", "myproject") - ), -]) +@pytest.mark.parametrize( + ("url", "expected"), + [ + ( + "http://git.mycompany.com/username/myproject.git", + ParsedGitUrl("http", "git.mycompany.com", "username", "myproject"), + ), + ( + "https://github.com/username/myproject.git", + ParsedGitUrl("https", "github.com", "username", "myproject"), + ), + ( + "https://gitlab.com/group/subgroup/myproject.git", + ParsedGitUrl("https", "gitlab.com", "group/subgroup", "myproject"), + ), + ( + "https://git.mycompany.com:4443/username/myproject.git", + ParsedGitUrl("https", "git.mycompany.com:4443", "username", "myproject"), + ), + ( + "git://host.xz/path/to/repo.git/", + ParsedGitUrl("git", "host.xz", "path/to", "repo"), + ), + ( + "git://host.xz:9418/path/to/repo.git/", + ParsedGitUrl("git", "host.xz:9418", "path/to", "repo"), + ), + ( + "git@github.com:username/myproject.git", + ParsedGitUrl("ssh", "git@github.com", "username", "myproject"), + ), + ( + "ssh://git@github.com:3759/myproject.git", + ParsedGitUrl("ssh", "git@github.com", "3759", "myproject"), + ), + ( + "ssh://git@github.com:username/myproject.git", + ParsedGitUrl("ssh", "git@github.com", "username", "myproject"), + ), + ( + "ssh://git@bitbucket.org:7999/username/myproject.git", + ParsedGitUrl("ssh", "git@bitbucket.org:7999", "username", "myproject"), + ), + ( + "git+ssh://git@github.com:username/myproject.git", + ParsedGitUrl("ssh", "git@github.com", "username", "myproject"), + ), + ( + "/Users/username/dev/remote/myproject.git", + ParsedGitUrl("file", "", "Users/username/dev/remote", "myproject"), + ), + ( + "file:///Users/username/dev/remote/myproject.git", + ParsedGitUrl("file", "", "Users/username/dev/remote", "myproject"), + ), + ( + "C:/Users/username/dev/remote/myproject.git", + ParsedGitUrl("file", "", "C:/Users/username/dev/remote", "myproject"), + ), + ( + "file:///C:/Users/username/dev/remote/myproject.git", + ParsedGitUrl("file", "", "C:/Users/username/dev/remote", "myproject"), + ), + ], +) def test_parse_valid_git_urls(url: str, expected: ParsedGitUrl): """Test that a valid given git remote url is parsed correctly.""" assert expected == parse_git_url(url) -@pytest.mark.parametrize('url', [ - "icmp://git", - "abcdefghijklmnop.git", - "../relative/path/to/repo.git", - "http://domain/project.git" -]) +@pytest.mark.parametrize( + "url", + [ + "icmp://git", + "abcdefghijklmnop.git", + "../relative/path/to/repo.git", + "http://domain/project.git", + ], +) def test_parse_invalid_git_urls(url: str): """Test that an invalid git remote url throws a ValueError.""" with pytest.raises(ValueError): From 38046d5e8c84443a088af6c94354ea4095c9ab70 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Fri, 22 Dec 2023 11:42:44 +0000 Subject: [PATCH 025/167] 8.6.0 Automatically generated by python-semantic-release --- CHANGELOG.md | 17 +++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2a720c53..e28dd92cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ +## v8.6.0 (2023-12-22) + +### Documentation + +* docs: minor correction to commit-parsing documentation (#777) ([`245e878`](https://github.com/python-semantic-release/python-semantic-release/commit/245e878f02d5cafec6baf0493c921c1e396b56e8)) + +### Feature + +* feat(utils): expand parsable valid git remote url formats (#771) + +Git remote url parsing now supports additional formats (ssh, https, file, git) ([`cf75f23`](https://github.com/python-semantic-release/python-semantic-release/commit/cf75f237360488ebb0088e5b8aae626e97d9cbdd)) + +### Style + +* style: beautify cf75f237360488ebb0088e5b8aae626e97d9cbdd ([`2de634d`](https://github.com/python-semantic-release/python-semantic-release/commit/2de634d6e1fed29e8ce55a1c57fd23bf838badd9)) + + ## v8.5.2 (2023-12-19) ### Build diff --git a/pyproject.toml b/pyproject.toml index 00f0cb94f..2d0f23b05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "8.5.2" +version = "8.6.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.7" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index b3340feaa..aa18f9ed7 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "8.5.2" +__version__ = "8.6.0" def setup_hook(argv: list[str]) -> None: From 26528eb8794d00dfe985812269702fbc4c4ec788 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 22 Dec 2023 06:50:04 -0500 Subject: [PATCH 026/167] feat(config): enable default environment token per hvcs (#774) --- semantic_release/cli/config.py | 48 +++++++++++-------- semantic_release/hvcs/__init__.py | 2 + semantic_release/hvcs/_base.py | 2 + semantic_release/hvcs/gitea.py | 1 + semantic_release/hvcs/github.py | 1 + semantic_release/hvcs/gitlab.py | 4 ++ .../unit/semantic_release/cli/test_config.py | 33 +++++++++++++ 7 files changed, 72 insertions(+), 19 deletions(-) diff --git a/semantic_release/cli/config.py b/semantic_release/cli/config.py index c8770691f..85fb4e30c 100644 --- a/semantic_release/cli/config.py +++ b/semantic_release/cli/config.py @@ -6,14 +6,15 @@ from dataclasses import dataclass from enum import Enum from pathlib import Path -from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union +from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type, Union from git import Actor from git.repo.base import Repo from jinja2 import Environment -from pydantic import BaseModel +from pydantic import BaseModel, model_validator from typing_extensions import Literal +from semantic_release import hvcs from semantic_release.changelog import environment from semantic_release.cli.const import DEFAULT_CONFIG_FILE from semantic_release.cli.masking_filter import MaskingFilter @@ -29,7 +30,6 @@ from semantic_release.const import COMMIT_MESSAGE, DEFAULT_COMMIT_AUTHOR, SEMVER_REGEX from semantic_release.errors import InvalidConfiguration, NotAReleaseBranch from semantic_release.helpers import dynamic_import -from semantic_release.hvcs import Gitea, Github, Gitlab, HvcsBase from semantic_release.version import VersionTranslator from semantic_release.version.declaration import ( PatternVersionDeclaration, @@ -46,6 +46,21 @@ class HvcsClient(str, Enum): GITEA = "gitea" +_known_commit_parsers = { + "angular": AngularCommitParser, + "emoji": EmojiCommitParser, + "scipy": ScipyCommitParser, + "tag": TagCommitParser, +} + + +_known_hvcs: Dict[HvcsClient, Type[hvcs.HvcsBase]] = { + HvcsClient.GITHUB: hvcs.Github, + HvcsClient.GITLAB: hvcs.Gitlab, + HvcsClient.GITEA: hvcs.Gitea, +} + + class EnvConfigVar(BaseModel): env: str default: Optional[str] = None @@ -90,13 +105,22 @@ class BranchConfig(BaseModel): class RemoteConfig(BaseModel): name: str = "origin" - token: MaybeFromEnv = EnvConfigVar(env="GH_TOKEN") + token: MaybeFromEnv = "" url: Optional[MaybeFromEnv] = None type: HvcsClient = HvcsClient.GITHUB domain: Optional[str] = None api_domain: Optional[str] = None ignore_token_for_push: bool = False + @model_validator(mode="after") + def set_default_token(self) -> "RemoteConfig": + # Set the default token name for the given VCS when no user input is given + if not self.token and self.type in _known_hvcs: + default_token_name = _known_hvcs[self.type].DEFAULT_ENV_TOKEN_NAME + if default_token_name: + self.token = EnvConfigVar(env=default_token_name) + return self + class PublishConfig(BaseModel): dist_glob_patterns: Tuple[str, ...] = ("dist/*",) @@ -172,20 +196,6 @@ def _recursive_getattr(obj: Any, path: str) -> Any: return out -_known_commit_parsers = { - "angular": AngularCommitParser, - "emoji": EmojiCommitParser, - "scipy": ScipyCommitParser, - "tag": TagCommitParser, -} - -_known_hvcs = { - HvcsClient.GITHUB: Github, - HvcsClient.GITLAB: Gitlab, - HvcsClient.GITEA: Gitea, -} - - @dataclass class RuntimeContext: _mask_attrs_: ClassVar[List[str]] = ["hvcs_client.token"] @@ -200,7 +210,7 @@ class RuntimeContext: commit_message: str changelog_excluded_commit_patterns: Tuple[re.Pattern[str], ...] version_declarations: Tuple[VersionDeclarationABC, ...] - hvcs_client: HvcsBase + hvcs_client: hvcs.HvcsBase changelog_file: Path ignore_token_for_push: bool template_environment: Environment diff --git a/semantic_release/hvcs/__init__.py b/semantic_release/hvcs/__init__.py index 067e7eb00..c3c69a1ac 100644 --- a/semantic_release/hvcs/__init__.py +++ b/semantic_release/hvcs/__init__.py @@ -3,3 +3,5 @@ from semantic_release.hvcs.github import Github from semantic_release.hvcs.gitlab import Gitlab from semantic_release.hvcs.token_auth import TokenAuth + +__all__ = ["Gitea", "Github", "Gitlab", "HvcsBase", "TokenAuth"] diff --git a/semantic_release/hvcs/_base.py b/semantic_release/hvcs/_base.py index 206b463e9..86cd29c4d 100644 --- a/semantic_release/hvcs/_base.py +++ b/semantic_release/hvcs/_base.py @@ -33,6 +33,8 @@ class HvcsBase: checking for NotImplemented around every method call. """ + DEFAULT_ENV_TOKEN_NAME = "HVCS_TOKEN" + def __init__( self, remote_url: str, diff --git a/semantic_release/hvcs/gitea.py b/semantic_release/hvcs/gitea.py index c4196ae51..4e4c28fdb 100644 --- a/semantic_release/hvcs/gitea.py +++ b/semantic_release/hvcs/gitea.py @@ -36,6 +36,7 @@ class Gitea(HvcsBase): DEFAULT_DOMAIN = "gitea.com" DEFAULT_API_PATH = "/api/v1" DEFAULT_API_DOMAIN = f"{DEFAULT_DOMAIN}{DEFAULT_API_PATH}" + DEFAULT_ENV_TOKEN_NAME = "GITEA_TOKEN" # pylint: disable=super-init-not-called def __init__( diff --git a/semantic_release/hvcs/github.py b/semantic_release/hvcs/github.py index f888595f2..11a8bae8b 100644 --- a/semantic_release/hvcs/github.py +++ b/semantic_release/hvcs/github.py @@ -37,6 +37,7 @@ class Github(HvcsBase): DEFAULT_DOMAIN = "github.com" DEFAULT_API_DOMAIN = "api.github.com" DEFAULT_UPLOAD_DOMAIN = "uploads.github.com" + DEFAULT_ENV_TOKEN_NAME = "GH_TOKEN" def __init__( self, diff --git a/semantic_release/hvcs/gitlab.py b/semantic_release/hvcs/gitlab.py index 1d684a502..1e4eb300c 100644 --- a/semantic_release/hvcs/gitlab.py +++ b/semantic_release/hvcs/gitlab.py @@ -38,6 +38,10 @@ class Gitlab(HvcsBase): API domain """ + DEFAULT_ENV_TOKEN_NAME = "GITLAB_TOKEN" + # purposefully not CI_JOB_TOKEN as it is not a personal access token, + # It is missing the permission to push to the repository, but has all others (releases, packages, etc.) + DEFAULT_DOMAIN = "gitlab.com" def __init__( diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index 9ead77eaf..166c2104e 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -1,15 +1,48 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING from unittest import mock import pytest import tomlkit +from pydantic import ValidationError from semantic_release.cli.config import ( + EnvConfigVar, GlobalCommandLineOptions, + HvcsClient, RawConfig, RuntimeContext, ) from semantic_release.const import DEFAULT_COMMIT_AUTHOR +if TYPE_CHECKING: + from typing import Any + + +@pytest.mark.parametrize("remote_config, expected_token", [ + ({ "type": HvcsClient.GITHUB.value }, EnvConfigVar(env="GH_TOKEN")), + ({ "type": HvcsClient.GITLAB.value }, EnvConfigVar(env="GITLAB_TOKEN")), + ({ "type": HvcsClient.GITEA.value }, EnvConfigVar(env="GITEA_TOKEN")), + ({}, EnvConfigVar(env="GH_TOKEN")), # default not provided -> means Github +]) +def test_load_hvcs_default_token(remote_config: dict[str, Any], expected_token): + raw_config = RawConfig.model_validate({ + "remote": remote_config, + }) + assert expected_token == raw_config.remote.token + + +@pytest.mark.parametrize("remote_config", [ + { "type": "nonexistent" } +]) +def test_invalid_hvcs_type(remote_config: dict[str, Any]): + with pytest.raises(ValidationError) as excinfo: + RawConfig.model_validate({ + "remote": remote_config, + }) + assert "remote.type" in str(excinfo.value) + def test_default_toml_config_valid(example_project): default_config_file = example_project / "default.toml" From 514f5580fbec0143f88d3f637be260c769136377 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 22 Dec 2023 11:55:06 +0000 Subject: [PATCH 027/167] style: beautify 26528eb8794d00dfe985812269702fbc4c4ec788 --- .../unit/semantic_release/cli/test_config.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index 166c2104e..2e0133de4 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -20,27 +20,32 @@ from typing import Any -@pytest.mark.parametrize("remote_config, expected_token", [ - ({ "type": HvcsClient.GITHUB.value }, EnvConfigVar(env="GH_TOKEN")), - ({ "type": HvcsClient.GITLAB.value }, EnvConfigVar(env="GITLAB_TOKEN")), - ({ "type": HvcsClient.GITEA.value }, EnvConfigVar(env="GITEA_TOKEN")), - ({}, EnvConfigVar(env="GH_TOKEN")), # default not provided -> means Github -]) +@pytest.mark.parametrize( + "remote_config, expected_token", + [ + ({"type": HvcsClient.GITHUB.value}, EnvConfigVar(env="GH_TOKEN")), + ({"type": HvcsClient.GITLAB.value}, EnvConfigVar(env="GITLAB_TOKEN")), + ({"type": HvcsClient.GITEA.value}, EnvConfigVar(env="GITEA_TOKEN")), + ({}, EnvConfigVar(env="GH_TOKEN")), # default not provided -> means Github + ], +) def test_load_hvcs_default_token(remote_config: dict[str, Any], expected_token): - raw_config = RawConfig.model_validate({ - "remote": remote_config, - }) + raw_config = RawConfig.model_validate( + { + "remote": remote_config, + } + ) assert expected_token == raw_config.remote.token -@pytest.mark.parametrize("remote_config", [ - { "type": "nonexistent" } -]) +@pytest.mark.parametrize("remote_config", [{"type": "nonexistent"}]) def test_invalid_hvcs_type(remote_config: dict[str, Any]): with pytest.raises(ValidationError) as excinfo: - RawConfig.model_validate({ - "remote": remote_config, - }) + RawConfig.model_validate( + { + "remote": remote_config, + } + ) assert "remote.type" in str(excinfo.value) From c1059f5b90895f75bde3d088bdc69e734e983535 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Fri, 22 Dec 2023 11:57:20 +0000 Subject: [PATCH 028/167] 8.7.0 Automatically generated by python-semantic-release --- CHANGELOG.md | 11 +++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e28dd92cb..73222c09a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ +## v8.7.0 (2023-12-22) + +### Feature + +* feat(config): enable default environment token per hvcs (#774) ([`26528eb`](https://github.com/python-semantic-release/python-semantic-release/commit/26528eb8794d00dfe985812269702fbc4c4ec788)) + +### Style + +* style: beautify 26528eb8794d00dfe985812269702fbc4c4ec788 ([`514f558`](https://github.com/python-semantic-release/python-semantic-release/commit/514f5580fbec0143f88d3f637be260c769136377)) + + ## v8.6.0 (2023-12-22) ### Documentation diff --git a/pyproject.toml b/pyproject.toml index 2d0f23b05..3162c440c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "8.6.0" +version = "8.7.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.7" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index aa18f9ed7..29c107720 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "8.6.0" +__version__ = "8.7.0" def setup_hook(argv: list[str]) -> None: From 0b07cae71915c5c82d7784898b44359249542a64 Mon Sep 17 00:00:00 2001 From: bernardcooke53 <66492393+bernardcooke53@users.noreply.github.com> Date: Fri, 22 Dec 2023 12:02:40 +0000 Subject: [PATCH 029/167] docs: add note on default envvar behaviour (#780) --- docs/configuration.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index a3c136d93..75fa0eacd 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -578,7 +578,16 @@ environment variable if you wish. configuring the environment variable for your remote VCS authentication token. -**Default:** ``{ env = "GH_TOKEN" }`` +The default value for this setting depends on what you specify as +:ref:`remote.type `. If you set ``remote.type = "github"``, +the default value for ``remote.token`` will be ``{ env = "GH_TOKEN" }``; +if you set ``remote.type = "gitlab"``, the default value will be +``{ env = "GITLAB_TOKEN" }``; and if you set ``remote.type = "gitea"``, the +default value of ``remote.token`` will be ``{ env = "GITEA_TOKEN" }``. + + +**Default:** ``{ env = "" }``, where ```` depends on +:ref:`remote.type ` as indicated above. .. _config-publish: From d6c4ae0db458f8108c88d75ac4e07079bc747d32 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 3 Jan 2024 13:46:26 -0500 Subject: [PATCH 030/167] test(infrastructure): refactor test fixtures & configuration for higher resiliency (#773) --- pyproject.toml | 6 +- tests/command_line/test_changelog.py | 35 ++- tests/command_line/test_version.py | 56 +++-- tests/const.py | 150 +---------- tests/fixtures/example_project.py | 106 +++++++- tests/fixtures/git_repo.py | 358 ++++++++++++++++++++------- 6 files changed, 429 insertions(+), 282 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3162c440c..1b5e069e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,7 @@ docs = [ test = [ "coverage[toml]>=6,<8", "pytest>=7,<8", + "pytest-env~=1.0", "pytest-xdist>=2,<4", "pytest-mock>=3,<4", "pytest-lazy-fixture~=0.6.3", @@ -71,9 +72,13 @@ dev = ["pre-commit", "tox", "ruff==0.1.8"] mypy = ["mypy", "types-requests"] [tool.pytest.ini_options] +env = [ + "PYTHONHASHSEED = 123456" +] addopts = [ "-nauto", "-ra", + "--diff-symbols", "--cache-clear", "--cov=semantic_release", "--cov-context=test", @@ -108,7 +113,6 @@ passenv = CI setenv = PYTHONPATH = {toxinidir} TESTING = True - PYTHONHASHSEED = 123456 deps = .[test] commands = coverage run -p --source=semantic_release -m pytest {posargs:tests} diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 19e1fc214..c61e2d7c1 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -22,12 +22,17 @@ from tests.util import flatten_dircmp if TYPE_CHECKING: + from pathlib import Path + from click.testing import CliRunner + from git import Repo from requests_mock import Mocker from semantic_release.changelog.release_history import ReleaseHistory from semantic_release.cli.config import RuntimeContext + from tests.fixtures.example_project import ExProjectDir + @pytest.mark.parametrize( "repo,tag", @@ -48,7 +53,12 @@ ) @pytest.mark.parametrize("arg0", [None, "--post-to-release-tag"]) def test_changelog_noop_is_noop( - repo, tag, arg0, tmp_path_factory, example_project, cli_runner + repo: Repo, + tag: str | None, + arg0: str | None, + tmp_path_factory: pytest.TempPathFactory, + example_project: ExProjectDir, + cli_runner: CliRunner, ): args = [arg0, tag] if tag and arg0 else [] tempdir = tmp_path_factory.mktemp("test_noop") @@ -72,7 +82,7 @@ def test_changelog_noop_is_noop( "semantic_release.hvcs.github.build_requests_session", return_value=session, ), requests_mock.Mocker(session=session) as mocker: - result = cli_runner.invoke(main, ["--noop", changelog.name, *args]) + result = cli_runner.invoke(main, ["--noop", changelog.name or "changelog", *args]) assert result.exit_code == 0 @@ -98,7 +108,11 @@ def test_changelog_noop_is_noop( ], ) def test_changelog_content_regenerated( - repo, tmp_path_factory, example_project, example_changelog_md, cli_runner + repo: Repo, + tmp_path_factory: pytest.TempPathFactory, + example_project: ExProjectDir, + example_changelog_md: Path, + cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_changelog") shutil.rmtree(str(tempdir.resolve())) @@ -107,7 +121,7 @@ def test_changelog_content_regenerated( # Remove the changelog and then check that we can regenerate it os.remove(str(example_changelog_md.resolve())) - result = cli_runner.invoke(main, [changelog.name]) + result = cli_runner.invoke(main, [changelog.name or "changelog"]) assert result.exit_code == 0 dcmp = filecmp.dircmp(str(example_project.resolve()), tempdir) @@ -122,13 +136,16 @@ def test_changelog_content_regenerated( "args", [("--post-to-release-tag", "v1.99.91910000000000000000000000000")] ) def test_changelog_release_tag_not_in_history( - args, tmp_path_factory, example_project, cli_runner + args: list[str], + tmp_path_factory: pytest.TempPathFactory, + example_project: ExProjectDir, + cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_changelog") shutil.rmtree(str(tempdir.resolve())) shutil.copytree(src=str(example_project.resolve()), dst=tempdir) - result = cli_runner.invoke(main, [changelog.name, *args]) + result = cli_runner.invoke(main, [changelog.name or "changelog", *args]) assert result.exit_code == 2 assert "not in release history" in result.stderr.lower() @@ -136,7 +153,11 @@ def test_changelog_release_tag_not_in_history( @pytest.mark.usefixtures("repo_with_single_branch_and_prereleases_angular_commits") @pytest.mark.parametrize("args", [("--post-to-release-tag", "v0.1.0")]) def test_changelog_post_to_release( - args, monkeypatch, tmp_path_factory, example_project, cli_runner + args: list[str], + monkeypatch: pytest.MonkeyPatch, + tmp_path_factory: pytest.TempPathFactory, + example_project: ExProjectDir, + cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_changelog") shutil.rmtree(str(tempdir.resolve())) diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 2bc6bdeda..f29bf6c25 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -4,7 +4,6 @@ import filecmp import re import shutil -from pathlib import Path from subprocess import CompletedProcess from typing import TYPE_CHECKING from unittest import mock @@ -23,13 +22,17 @@ ) if TYPE_CHECKING: + from pathlib import Path from unittest.mock import MagicMock from click.testing import CliRunner + from git import Repo from requests_mock import Mocker from semantic_release.cli.config import RuntimeContext + from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn + @pytest.mark.parametrize( "repo", @@ -384,13 +387,13 @@ def test_version_already_released_no_push(repo, cli_runner): ], ) def test_version_no_push_force_level( - repo, - cli_args, - expected_new_version, - example_project, - example_pyproject_toml, + repo: Repo, + cli_args: list[str], + expected_new_version: str, + example_project: ExProjectDir, + example_pyproject_toml: Path, tmp_path_factory: pytest.TempPathFactory, - cli_runner, + cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_version") shutil.rmtree(str(tempdir.resolve())) @@ -398,7 +401,7 @@ def test_version_no_push_force_level( head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) - result = cli_runner.invoke(main, [version.name, *cli_args, "--no-push"]) + result = cli_runner.invoke(main, [version.name or "version", *cli_args, "--no-push"]) tags_after = sorted(repo.tags, key=lambda tag: tag.name) head_after = repo.head.commit @@ -466,26 +469,27 @@ def test_version_no_push_force_level( lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], ) -def test_version_build_metadata_triggers_new_version(repo, cli_runner): +def test_version_build_metadata_triggers_new_version(repo: Repo, cli_runner: CliRunner): + version_cmd_name = version.name or "version" # Verify we get "no version to release" without build metadata no_metadata_result = cli_runner.invoke( - main, ["--strict", version.name, "--no-push"] + main, ["--strict", version_cmd_name, "--no-push"] ) assert no_metadata_result.exit_code == 2 assert "no release will be made" in no_metadata_result.stderr.lower() metadata_suffix = "build.abc-12345" result = cli_runner.invoke( - main, [version.name, "--no-push", "--build-metadata", metadata_suffix] + main, [version_cmd_name, "--no-push", "--build-metadata", metadata_suffix] ) assert result.exit_code == 0 assert repo.git.tag(l=f"*{metadata_suffix}") def test_version_prints_current_version_if_no_new_version( - repo_with_git_flow_angular_commits, cli_runner + repo_with_git_flow_angular_commits: Repo, cli_runner: CliRunner ): - result = cli_runner.invoke(main, [version.name, "--no-push"]) + result = cli_runner.invoke(main, [version.name or "version", "--no-push"]) assert result.exit_code == 0 assert "no release will be made" in result.stderr.lower() assert result.stdout == "1.2.0-alpha.2\n" @@ -493,19 +497,27 @@ def test_version_prints_current_version_if_no_new_version( @pytest.mark.parametrize("shell", ("/usr/bin/bash", "/usr/bin/zsh", "powershell")) def test_version_runs_build_command( - repo_with_git_flow_angular_commits, cli_runner, example_pyproject_toml, shell + repo_with_git_flow_angular_commits: Repo, + cli_runner: CliRunner, + example_pyproject_toml: Path, + update_pyproject_toml: UpdatePyprojectTomlFn, + shell: str ): - config = tomlkit.loads(example_pyproject_toml.read_text(encoding="utf-8")) - build_command = config["tool"]["semantic_release"]["build_command"] # type: ignore[attr-defined] + # Setup + build_command = "bash -c \"echo 'hello world'\"" + update_pyproject_toml("tool.semantic_release.build_command", build_command) exe = shell.split("/")[-1] + + # Mock out subprocess.run with mock.patch( "subprocess.run", return_value=CompletedProcess(args=(), returncode=0) ) as patched_subprocess_run, mock.patch( "shellingham.detect_shell", return_value=(exe, shell) ): + # ACT: run & force a new version that will trigger the build command result = cli_runner.invoke( - main, [version.name, "--patch", "--no-push"] - ) # force a new version + main, [version.name or "version", "--patch", "--no-push"] + ) assert result.exit_code == 0 patched_subprocess_run.assert_called_once_with( @@ -571,7 +583,7 @@ def test_custom_release_notes_template( # (see fixtures) # Act - resp = cli_runner.invoke(main, [version.name, "--skip-build", "--vcs-release"]) + resp = cli_runner.invoke(main, [version.name or "version", "--skip-build", "--vcs-release"]) release_history = get_release_history_from_context(runtime_context_with_no_tags) tag = runtime_context_with_no_tags.repo.tags[-1].name release_version = runtime_context_with_no_tags.version_translator.from_tag(tag) @@ -625,15 +637,13 @@ def test_version_only_update_files_no_git_actions( cli_runner: CliRunner, tmp_path_factory: pytest.TempPathFactory, example_pyproject_toml: Path, + example_project: ExProjectDir ) -> None: # Arrange expected_new_version = "0.3.0" tempdir = tmp_path_factory.mktemp("test_version") shutil.rmtree(str(tempdir.resolve())) - example_project = Path( - runtime_context_with_tags.repo.git.rev_parse("--show-toplevel") - ) - shutil.copytree(src=str(example_project.resolve()), dst=tempdir) + shutil.copytree(src=str(example_project), dst=tempdir) head_before = runtime_context_with_tags.repo.head.commit tags_before = runtime_context_with_tags.repo.tags diff --git a/tests/const.py b/tests/const.py index 90a7c6dec..44e4e93ae 100644 --- a/tests/const.py +++ b/tests/const.py @@ -1,4 +1,3 @@ -from semantic_release.const import DEFAULT_COMMIT_AUTHOR A_FULL_VERSION_STRING = "1.11.567" A_PRERELEASE_VERSION_STRING = "2.3.4-dev.23" @@ -127,6 +126,9 @@ EXAMPLE_PROJECT_NAME = "example" EXAMPLE_PROJECT_VERSION = "0.2.2" +# Uses the internal defaults of semantic-release unless otherwise needed for testing +# modify the pyproject toml as necessary for the test using update_pyproject_toml() +# and derivative fixtures EXAMPLE_PYPROJECT_TOML_CONTENT = rf""" [tool.poetry] name = "{EXAMPLE_PROJECT_NAME}" @@ -136,44 +138,11 @@ authors = ["semantic-release "] readme = "README.md" classifiers = [ - "Development Status :: 2 - Pre-Alpha", - "Framework :: FastAPI", - "Framework :: Pytest", - "Intended Audience :: Education", "Natural Language :: English", "Operating System :: OS Independent", - "Programming Language :: Python :: 3 :: Only", - "Topic :: Education", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Server", + "Programming Language :: Python :: 3 :: Only" ] -[tool.poetry.urls] -"Repository" = "https://github.com/python-semantic-release/python-semantic-release" -"Bug Tracker" = "https://github.com/python-semantic-release/python-semantic-release" -"Homepage" = "https://github.com/python-semantic-release/python-semantic-release" - -[tool.poetry.scripts] -hello-world = "hello-world:main" - -[tool.poetry.dependencies] -python = "^3.8" -fastapi = "^0.74.0" -uvicorn = "^0.17.5" -PyYAML = "^6.0" -python-dotenv = "^0.19.2" -motor = "^2.5.1" -pymongo = "^3.12.0" - -[tool.poetry.dev-dependencies] -pytest = "^6.2" -bandit = "^1.7.2" -mypy = "0.931" -black = "^22.1.0" -safety = "^1.10.3" -flake8 = "^4.0.1" -types-PyYAML = "^6.0.4" -python-semantic-release = "^7.25.2" - [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" @@ -183,117 +152,6 @@ "src/{EXAMPLE_PROJECT_NAME}/_version.py:__version__", ] version_toml = ["pyproject.toml:tool.poetry.version"] -build_command = "bash -c \"echo 'Hello World'\"" -tag_format = "v{{version}}" -commit_parser = "angular" -commit_author = {{ env = "GIT_COMMIT_AUTHOR", default = "{DEFAULT_COMMIT_AUTHOR}" }} -commit_message = "{{version}}\n\nAutomatically generated by python-semantic-release" -major_on_zero = true -assets = [] - -[tool.semantic_release.commit_parser_options] -allowed_tags = [ - "build", - "chore", - "ci", - "docs", - "feat", - "fix", - "perf", - "style", - "refactor", - "test", -] -minor_tags = ["feat"] -patch_tags = ["fix", "perf"] - -[tool.semantic_release.changelog] -template_dir = "templates" -default_output_file = "TEST_CHANGELOG.md" - -[tool.semantic_release.changelog.environment] -block_start_string = "{{%" -block_end_string = "%}}" -variable_start_string = "{{{{" -variable_end_string = "}}}}" -comment_start_string = "{{#" -comment_end_string = "#}}" -trim_blocks = false -lstrip_blocks = false -newline_sequence = "\n" -keep_trailing_newline = false -extensions = [] -autoescape = true - -[tool.semantic_release.branches.main] -match = "(main|master)" -prerelease = false -prerelease_token = "rc" - -[tool.semantic_release.branches.release-candidates] -match = "rc-.*" -prerelease = true -prerelease_token = "rc" - -[tool.semantic_release.branches.features] -match = "feat.*" -prerelease = true -prerelease_token = "alpha" - -[tool.semantic_release.branches.beta-testing] -match = "beta.*" -prerelease = true -prerelease_token = "beta" - -[tool.semantic_release.remote] -name = "origin" -type = "github" -ignore_token_for_push = false - -[tool.semantic_release.publish] -dist_glob_patterns = ["dist/*"] -upload_to_vcs_release = false - -[tool.isort] -profile = "black" -src_paths = ["src"] -known_first_party = "{EXAMPLE_PROJECT_NAME}" -known_third_party = ["fastapi", "pydantic", "motor", "bson", "uvicorn"] -combine_as_imports = true - -[tool.mypy] -python_version=3.7 - -mypy_path="src" - -show_column_numbers=true -show_error_context=true -pretty=true -error_summary=true - -follow_imports="normal" -ignore_missing_imports=true - -disallow_untyped_calls=true -warn_return_any=true -strict_optional=true -warn_no_return=true -warn_redundant_casts=true -warn_unused_ignores=true -warn_unused_configs=true -disallow_any_generics=true - -warn_unreachable=true -disallow_untyped_defs=true -check_untyped_defs=true - -cache_dir="/dev/null" - -[[tool.mypy.overrides]] -module = "tests.*" -allow_untyped_defs = true -allow_incomplete_defs = true -allow_untyped_calls = true """ EXAMPLE_SETUP_CFG_CONTENT = rf""" diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 2a5fb47e5..e80ef30f4 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from pathlib import Path from textwrap import dedent @@ -6,6 +8,9 @@ import pytest import tomlkit +from semantic_release.commit_parser import * +from semantic_release.hvcs import * + from tests.const import ( EXAMPLE_CHANGELOG_MD_CONTENT, EXAMPLE_PROJECT_NAME, @@ -22,12 +27,20 @@ ExProjectDir = Path class UpdatePyprojectTomlFn(Protocol): - def __call__(self, setting: str, value: "Any") -> None: + def __call__(self, setting: str, value: Any) -> None: + ... + + class UseHvcsFn(Protocol): + def __call__(self) -> type[HvcsBase]: + ... + + class UseParserFn(Protocol): + def __call__(self) -> type[CommitParser]: ... @pytest.fixture -def change_to_tmp_dir(tmp_path: "Path") -> "Generator[None, None, None]": +def change_to_tmp_dir(tmp_path: Path) -> Generator[None, None, None]: cwd = os.getcwd() os.chdir(str(tmp_path.resolve())) try: @@ -37,7 +50,7 @@ def change_to_tmp_dir(tmp_path: "Path") -> "Generator[None, None, None]": @pytest.fixture -def example_project(change_to_tmp_dir: None) -> "ExProjectDir": +def example_project(change_to_tmp_dir: None) -> ExProjectDir: tmp_path = Path.cwd() src_dir = tmp_path / "src" src_dir.mkdir() @@ -97,38 +110,37 @@ def example_project_with_release_notes_template(example_project: Path) -> Path: @pytest.fixture -def example_pyproject_toml(example_project): +def example_pyproject_toml(example_project: ExProjectDir) -> Path: return example_project / "pyproject.toml" @pytest.fixture -def example_setup_cfg(example_project): +def example_setup_cfg(example_project: ExProjectDir) -> Path: return example_project / "setup.cfg" @pytest.fixture -def example_setup_py(example_project): +def example_setup_py(example_project: ExProjectDir) -> Path: return example_project / "setup.py" # Note this is just the path and the content may change @pytest.fixture -def example_changelog_md(example_project): +def example_changelog_md(example_project: ExProjectDir) -> Path: return example_project / "CHANGELOG.md" @pytest.fixture -def example_project_template_dir(example_project): +def example_project_template_dir(example_project: ExProjectDir) -> Path: return example_project / "templates" @pytest.fixture def update_pyproject_toml( - example_project: "Path", example_pyproject_toml: "Path" -) -> "UpdatePyprojectTomlFn": + example_project: Path, example_pyproject_toml: Path +) -> UpdatePyprojectTomlFn: """Update the pyproject.toml file with the given content.""" - - def _update_pyproject_toml(setting: str, value: "Any") -> None: + def _update_pyproject_toml(setting: str, value: Any) -> None: with open(example_pyproject_toml) as rfd: pyproject_toml = tomlkit.load(rfd) @@ -148,3 +160,73 @@ def _update_pyproject_toml(setting: str, value: "Any") -> None: tomlkit.dump(pyproject_toml, wfd) return _update_pyproject_toml + + +@pytest.fixture +def use_angular_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: + """Modify the configuration file to use the Angular parser.""" + def _use_angular_parser() -> type[CommitParser]: + update_pyproject_toml("tool.semantic_release.commit_parser", "angular") + return AngularCommitParser + + return _use_angular_parser + + +@pytest.fixture +def use_emoji_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: + """Modify the configuration file to use the Emoji parser.""" + def _use_emoji_parser() -> type[CommitParser]: + update_pyproject_toml("tool.semantic_release.commit_parser", "emoji") + return EmojiCommitParser + + return _use_emoji_parser + + +@pytest.fixture +def use_scipy_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: + """Modify the configuration file to use the Scipy parser.""" + def _use_scipy_parser() -> type[CommitParser]: + update_pyproject_toml("tool.semantic_release.commit_parser", "scipy") + return ScipyCommitParser + + return _use_scipy_parser + + +@pytest.fixture +def use_tag_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: + """Modify the configuration file to use the Tag parser.""" + def _use_tag_parser() -> type[CommitParser]: + update_pyproject_toml("tool.semantic_release.commit_parser", "tag") + return TagCommitParser + + return _use_tag_parser + + +@pytest.fixture +def use_github_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: + """Modify the configuration file to use GitHub as the HVCS.""" + def _use_github_hvcs() -> type[HvcsBase]: + update_pyproject_toml("tool.semantic_release.remote.type", "github") + return Github + + return _use_github_hvcs + + +@pytest.fixture +def use_gitlab_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: + """Modify the configuration file to use GitLab as the HVCS.""" + def _use_gitlab_hvcs() -> type[HvcsBase]: + update_pyproject_toml("tool.semantic_release.remote.type", "gitlab") + return Gitlab + + return _use_gitlab_hvcs + + +@pytest.fixture +def use_gitea_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: + """Modify the configuration file to use Gitea as the HVCS.""" + def _use_gitea_hvcs() -> type[HvcsBase]: + update_pyproject_toml("tool.semantic_release.remote.type", "gitea") + return Gitea + + return _use_gitea_hvcs diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 67b0e68ca..9f1fcdf75 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + import pytest from git import Actor, Repo from pytest_lazyfixture import lazy_fixture @@ -5,6 +9,19 @@ from tests.const import COMMIT_MESSAGE, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER from tests.util import add_text_to_file, shortuid +if TYPE_CHECKING: + from typing import Generator, Protocol + + from tests.fixtures.example_project import ( + ExProjectDir, + UpdatePyprojectTomlFn, + UseParserFn, + ) + + class RepoInitFn(Protocol): + def __call__(self) -> Repo: + ... + @pytest.fixture def commit_author(): @@ -31,17 +48,18 @@ def example_git_https_url(): # twice, once with a different URL format params=[lazy_fixture("example_git_ssh_url")] ) -def git_repo_factory(request, example_project): - """ - !!! WARNING !!! - You must call repo.close() after yield-ing the result of - calling this factory in a test, otherwise the test suite will fail with - OSError: Too Many Open Files - See https://github.com/pytest-dev/pytest/issues/2970#issuecomment-348033023 - """ - - def git_repo(): +def git_repo_factory( + request: pytest.FixtureRequest, + example_project: ExProjectDir +) -> Generator[RepoInitFn, None, None]: + repos: list[Repo] = [] + + def git_repo() -> Repo: repo = Repo.init(example_project.resolve()) + + # store the repo so we can close it later + repos.append(repo) + # Without this the global config may set it to "master", we want consistency repo.git.branch("-M", "main") with repo.config_writer("repository") as config: @@ -51,13 +69,24 @@ def git_repo(): repo.create_remote(name="origin", url=request.param) return repo - return git_repo + try: + yield git_repo + finally: + for repo in repos: + repo.close() @pytest.fixture -def repo_with_no_tags_angular_commits(git_repo_factory, file_in_repo): +def repo_with_no_tags_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_angular_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -67,14 +96,20 @@ def repo_with_no_tags_angular_commits(git_repo_factory, file_in_repo): add_text_to_file(git_repo, file_in_repo) git_repo.git.commit(m="fix: more text") - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_no_tags_emoji_commits(git_repo_factory, file_in_repo): +def repo_with_no_tags_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_emoji_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -84,14 +119,20 @@ def repo_with_no_tags_emoji_commits(git_repo_factory, file_in_repo): add_text_to_file(git_repo, file_in_repo) git_repo.git.commit(m=":bug: more text") - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_no_tags_scipy_commits(git_repo_factory, file_in_repo): +def repo_with_no_tags_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_scipy_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -101,14 +142,20 @@ def repo_with_no_tags_scipy_commits(git_repo_factory, file_in_repo): add_text_to_file(git_repo, file_in_repo) git_repo.git.commit(m="MAINT: more text") - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_no_tags_tag_commits(git_repo_factory, file_in_repo): +def repo_with_no_tags_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_tag_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -118,14 +165,20 @@ def repo_with_no_tags_tag_commits(git_repo_factory, file_in_repo): add_text_to_file(git_repo, file_in_repo) git_repo.git.commit(m=":nut_and_bolt: more text") - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_single_branch_angular_commits(git_repo_factory, file_in_repo): +def repo_with_single_branch_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_angular_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -139,14 +192,20 @@ def repo_with_single_branch_angular_commits(git_repo_factory, file_in_repo): git_repo.git.tag("v0.1.1", m="v0.1.1") assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_single_branch_emoji_commits(git_repo_factory, file_in_repo): +def repo_with_single_branch_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_emoji_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -160,14 +219,20 @@ def repo_with_single_branch_emoji_commits(git_repo_factory, file_in_repo): git_repo.git.tag("v0.1.1", m="v0.1.1") assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_single_branch_scipy_commits(git_repo_factory, file_in_repo): +def repo_with_single_branch_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_scipy_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -181,14 +246,20 @@ def repo_with_single_branch_scipy_commits(git_repo_factory, file_in_repo): git_repo.git.tag("v0.1.1", m="v0.1.1") assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_single_branch_tag_commits(git_repo_factory, file_in_repo): +def repo_with_single_branch_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_tag_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -202,16 +273,20 @@ def repo_with_single_branch_tag_commits(git_repo_factory, file_in_repo): git_repo.git.tag("v0.1.1", m="v0.1.1") assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - yield git_repo - git_repo.close() + return git_repo @pytest.fixture def repo_with_single_branch_and_prereleases_angular_commits( - git_repo_factory, file_in_repo -): + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_angular_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -240,16 +315,20 @@ def repo_with_single_branch_and_prereleases_angular_commits( git_repo.git.tag("v0.2.0", m="v0.2.0") assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - yield git_repo - git_repo.close() + return git_repo @pytest.fixture def repo_with_single_branch_and_prereleases_emoji_commits( - git_repo_factory, file_in_repo -): + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_emoji_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -278,16 +357,20 @@ def repo_with_single_branch_and_prereleases_emoji_commits( git_repo.git.tag("v0.2.0", m="v0.2.0") assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - yield git_repo - git_repo.close() + return git_repo @pytest.fixture def repo_with_single_branch_and_prereleases_scipy_commits( - git_repo_factory, file_in_repo -): + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_scipy_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -316,14 +399,20 @@ def repo_with_single_branch_and_prereleases_scipy_commits( git_repo.git.tag("v0.2.0", m="v0.2.0") assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_single_branch_and_prereleases_tag_commits(git_repo_factory, file_in_repo): +def repo_with_single_branch_and_prereleases_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_tag_parser() + add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -352,15 +441,29 @@ def repo_with_single_branch_and_prereleases_tag_commits(git_repo_factory, file_i git_repo.git.tag("v0.2.0", m="v0.2.0") assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_main_and_feature_branches_angular_commits(git_repo_factory, file_in_repo): +def repo_with_main_and_feature_branches_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + file_in_repo: str, +) -> Repo: git_repo = git_repo_factory() + use_angular_parser() + update_pyproject_toml( + "tool.semantic_release.branches.beta-testing", + { + "match": "beta.*", + "prerelease": True, + "prerelease_token": "beta" + } + ) add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -402,15 +505,20 @@ def repo_with_main_and_feature_branches_angular_commits(git_repo_factory, file_i assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "beta_testing" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_main_and_feature_branches_emoji_commits(git_repo_factory, file_in_repo): +def repo_with_main_and_feature_branches_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_emoji_parser() add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -452,15 +560,20 @@ def repo_with_main_and_feature_branches_emoji_commits(git_repo_factory, file_in_ assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "beta_testing" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_main_and_feature_branches_scipy_commits(git_repo_factory, file_in_repo): +def repo_with_main_and_feature_branches_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_scipy_parser() add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -502,15 +615,20 @@ def repo_with_main_and_feature_branches_scipy_commits(git_repo_factory, file_in_ assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "beta_testing" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_main_and_feature_branches_tag_commits(git_repo_factory, file_in_repo): +def repo_with_main_and_feature_branches_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_tag_parser() add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -552,15 +670,29 @@ def repo_with_main_and_feature_branches_tag_commits(git_repo_factory, file_in_re assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "beta_testing" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_git_flow_angular_commits(git_repo_factory, file_in_repo): +def repo_with_git_flow_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_angular_parser() + update_pyproject_toml( + "tool.semantic_release.branches.features", + { + "match": "feat.*", + "prerelease": True, + "prerelease_token": "alpha" + } + ) add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -627,15 +759,20 @@ def repo_with_git_flow_angular_commits(git_repo_factory, file_in_repo): assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "feature" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_git_flow_emoji_commits(git_repo_factory, file_in_repo): +def repo_with_git_flow_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_emoji_parser() add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -702,15 +839,20 @@ def repo_with_git_flow_emoji_commits(git_repo_factory, file_in_repo): assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "feature" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_git_flow_scipy_commits(git_repo_factory, file_in_repo): +def repo_with_git_flow_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_scipy_parser() add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -777,15 +919,20 @@ def repo_with_git_flow_scipy_commits(git_repo_factory, file_in_repo): assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "feature" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_git_flow_tag_commits(git_repo_factory, file_in_repo): +def repo_with_git_flow_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_tag_parser() add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -854,17 +1001,29 @@ def repo_with_git_flow_tag_commits(git_repo_factory, file_in_repo): assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "feature" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture def repo_with_git_flow_and_release_channels_angular_commits( - git_repo_factory, file_in_repo -): + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_angular_parser() + update_pyproject_toml( + "tool.semantic_release.branches.features", + { + "match": "feat.*", + "prerelease": True, + "prerelease_token": "alpha" + } + ) add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -937,17 +1096,20 @@ def repo_with_git_flow_and_release_channels_angular_commits( assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "feature" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( - git_repo_factory, file_in_repo -): + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_angular_parser() # TODO: is this correct? add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -1020,17 +1182,20 @@ def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( assert git_repo.commit("vpy1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "feature" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture def repo_with_git_flow_and_release_channels_emoji_commits( - git_repo_factory, file_in_repo -): + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_emoji_parser() add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -1103,17 +1268,20 @@ def repo_with_git_flow_and_release_channels_emoji_commits( assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "feature" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture def repo_with_git_flow_and_release_channels_scipy_commits( - git_repo_factory, file_in_repo -): + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_scipy_parser() add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -1186,15 +1354,20 @@ def repo_with_git_flow_and_release_channels_scipy_commits( assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "feature" - yield git_repo - git_repo.close() + return git_repo @pytest.fixture -def repo_with_git_flow_and_release_channels_tag_commits(git_repo_factory, file_in_repo): +def repo_with_git_flow_and_release_channels_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str +) -> Repo: git_repo = git_repo_factory() + use_tag_parser() add_text_to_file(git_repo, file_in_repo) + git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -1269,5 +1442,4 @@ def repo_with_git_flow_and_release_channels_tag_commits(git_repo_factory, file_i assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha assert git_repo.active_branch.name == "feature" - yield git_repo - git_repo.close() + return git_repo From 253c99e72c1f4ddefd806c87cae10d1b72ff461b Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 3 Jan 2024 18:51:28 +0000 Subject: [PATCH 031/167] style: beautify d6c4ae0db458f8108c88d75ac4e07079bc747d32 --- tests/command_line/test_changelog.py | 4 +- tests/command_line/test_version.py | 12 ++- tests/const.py | 1 - tests/fixtures/example_project.py | 8 ++ tests/fixtures/git_repo.py | 115 +++++++-------------------- 5 files changed, 48 insertions(+), 92 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index c61e2d7c1..045e4fb99 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -82,7 +82,9 @@ def test_changelog_noop_is_noop( "semantic_release.hvcs.github.build_requests_session", return_value=session, ), requests_mock.Mocker(session=session) as mocker: - result = cli_runner.invoke(main, ["--noop", changelog.name or "changelog", *args]) + result = cli_runner.invoke( + main, ["--noop", changelog.name or "changelog", *args] + ) assert result.exit_code == 0 diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index f29bf6c25..58a903f8b 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -401,7 +401,9 @@ def test_version_no_push_force_level( head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) - result = cli_runner.invoke(main, [version.name or "version", *cli_args, "--no-push"]) + result = cli_runner.invoke( + main, [version.name or "version", *cli_args, "--no-push"] + ) tags_after = sorted(repo.tags, key=lambda tag: tag.name) head_after = repo.head.commit @@ -501,7 +503,7 @@ def test_version_runs_build_command( cli_runner: CliRunner, example_pyproject_toml: Path, update_pyproject_toml: UpdatePyprojectTomlFn, - shell: str + shell: str, ): # Setup build_command = "bash -c \"echo 'hello world'\"" @@ -583,7 +585,9 @@ def test_custom_release_notes_template( # (see fixtures) # Act - resp = cli_runner.invoke(main, [version.name or "version", "--skip-build", "--vcs-release"]) + resp = cli_runner.invoke( + main, [version.name or "version", "--skip-build", "--vcs-release"] + ) release_history = get_release_history_from_context(runtime_context_with_no_tags) tag = runtime_context_with_no_tags.repo.tags[-1].name release_version = runtime_context_with_no_tags.version_translator.from_tag(tag) @@ -637,7 +641,7 @@ def test_version_only_update_files_no_git_actions( cli_runner: CliRunner, tmp_path_factory: pytest.TempPathFactory, example_pyproject_toml: Path, - example_project: ExProjectDir + example_project: ExProjectDir, ) -> None: # Arrange expected_new_version = "0.3.0" diff --git a/tests/const.py b/tests/const.py index 44e4e93ae..1810bb7b2 100644 --- a/tests/const.py +++ b/tests/const.py @@ -1,4 +1,3 @@ - A_FULL_VERSION_STRING = "1.11.567" A_PRERELEASE_VERSION_STRING = "2.3.4-dev.23" A_FULL_VERSION_STRING_WITH_BUILD_METADATA = "4.2.3+build.12345" diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index e80ef30f4..47ad02ce8 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -140,6 +140,7 @@ def update_pyproject_toml( example_project: Path, example_pyproject_toml: Path ) -> UpdatePyprojectTomlFn: """Update the pyproject.toml file with the given content.""" + def _update_pyproject_toml(setting: str, value: Any) -> None: with open(example_pyproject_toml) as rfd: pyproject_toml = tomlkit.load(rfd) @@ -165,6 +166,7 @@ def _update_pyproject_toml(setting: str, value: Any) -> None: @pytest.fixture def use_angular_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: """Modify the configuration file to use the Angular parser.""" + def _use_angular_parser() -> type[CommitParser]: update_pyproject_toml("tool.semantic_release.commit_parser", "angular") return AngularCommitParser @@ -175,6 +177,7 @@ def _use_angular_parser() -> type[CommitParser]: @pytest.fixture def use_emoji_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: """Modify the configuration file to use the Emoji parser.""" + def _use_emoji_parser() -> type[CommitParser]: update_pyproject_toml("tool.semantic_release.commit_parser", "emoji") return EmojiCommitParser @@ -185,6 +188,7 @@ def _use_emoji_parser() -> type[CommitParser]: @pytest.fixture def use_scipy_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: """Modify the configuration file to use the Scipy parser.""" + def _use_scipy_parser() -> type[CommitParser]: update_pyproject_toml("tool.semantic_release.commit_parser", "scipy") return ScipyCommitParser @@ -195,6 +199,7 @@ def _use_scipy_parser() -> type[CommitParser]: @pytest.fixture def use_tag_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: """Modify the configuration file to use the Tag parser.""" + def _use_tag_parser() -> type[CommitParser]: update_pyproject_toml("tool.semantic_release.commit_parser", "tag") return TagCommitParser @@ -205,6 +210,7 @@ def _use_tag_parser() -> type[CommitParser]: @pytest.fixture def use_github_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use GitHub as the HVCS.""" + def _use_github_hvcs() -> type[HvcsBase]: update_pyproject_toml("tool.semantic_release.remote.type", "github") return Github @@ -215,6 +221,7 @@ def _use_github_hvcs() -> type[HvcsBase]: @pytest.fixture def use_gitlab_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use GitLab as the HVCS.""" + def _use_gitlab_hvcs() -> type[HvcsBase]: update_pyproject_toml("tool.semantic_release.remote.type", "gitlab") return Gitlab @@ -225,6 +232,7 @@ def _use_gitlab_hvcs() -> type[HvcsBase]: @pytest.fixture def use_gitea_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use Gitea as the HVCS.""" + def _use_gitea_hvcs() -> type[HvcsBase]: update_pyproject_toml("tool.semantic_release.remote.type", "gitea") return Gitea diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 9f1fcdf75..e1338c997 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -49,8 +49,7 @@ def example_git_https_url(): params=[lazy_fixture("example_git_ssh_url")] ) def git_repo_factory( - request: pytest.FixtureRequest, - example_project: ExProjectDir + request: pytest.FixtureRequest, example_project: ExProjectDir ) -> Generator[RepoInitFn, None, None]: repos: list[Repo] = [] @@ -78,9 +77,7 @@ def git_repo() -> Repo: @pytest.fixture def repo_with_no_tags_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_angular_parser() @@ -101,9 +98,7 @@ def repo_with_no_tags_angular_commits( @pytest.fixture def repo_with_no_tags_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() @@ -124,9 +119,7 @@ def repo_with_no_tags_emoji_commits( @pytest.fixture def repo_with_no_tags_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() @@ -147,9 +140,7 @@ def repo_with_no_tags_scipy_commits( @pytest.fixture def repo_with_no_tags_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_tag_parser() @@ -170,9 +161,7 @@ def repo_with_no_tags_tag_commits( @pytest.fixture def repo_with_single_branch_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_angular_parser() @@ -197,9 +186,7 @@ def repo_with_single_branch_angular_commits( @pytest.fixture def repo_with_single_branch_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() @@ -224,9 +211,7 @@ def repo_with_single_branch_emoji_commits( @pytest.fixture def repo_with_single_branch_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() @@ -251,9 +236,7 @@ def repo_with_single_branch_scipy_commits( @pytest.fixture def repo_with_single_branch_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_tag_parser() @@ -278,9 +261,7 @@ def repo_with_single_branch_tag_commits( @pytest.fixture def repo_with_single_branch_and_prereleases_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_angular_parser() @@ -320,9 +301,7 @@ def repo_with_single_branch_and_prereleases_angular_commits( @pytest.fixture def repo_with_single_branch_and_prereleases_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() @@ -362,9 +341,7 @@ def repo_with_single_branch_and_prereleases_emoji_commits( @pytest.fixture def repo_with_single_branch_and_prereleases_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() @@ -404,9 +381,7 @@ def repo_with_single_branch_and_prereleases_scipy_commits( @pytest.fixture def repo_with_single_branch_and_prereleases_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_tag_parser() @@ -455,11 +430,7 @@ def repo_with_main_and_feature_branches_angular_commits( use_angular_parser() update_pyproject_toml( "tool.semantic_release.branches.beta-testing", - { - "match": "beta.*", - "prerelease": True, - "prerelease_token": "beta" - } + {"match": "beta.*", "prerelease": True, "prerelease_token": "beta"}, ) add_text_to_file(git_repo, file_in_repo) @@ -510,9 +481,7 @@ def repo_with_main_and_feature_branches_angular_commits( @pytest.fixture def repo_with_main_and_feature_branches_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() @@ -565,9 +534,7 @@ def repo_with_main_and_feature_branches_emoji_commits( @pytest.fixture def repo_with_main_and_feature_branches_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() @@ -620,9 +587,7 @@ def repo_with_main_and_feature_branches_scipy_commits( @pytest.fixture def repo_with_main_and_feature_branches_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_tag_parser() @@ -678,17 +643,13 @@ def repo_with_git_flow_angular_commits( git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, update_pyproject_toml: UpdatePyprojectTomlFn, - file_in_repo: str + file_in_repo: str, ) -> Repo: git_repo = git_repo_factory() use_angular_parser() update_pyproject_toml( "tool.semantic_release.branches.features", - { - "match": "feat.*", - "prerelease": True, - "prerelease_token": "alpha" - } + {"match": "feat.*", "prerelease": True, "prerelease_token": "alpha"}, ) add_text_to_file(git_repo, file_in_repo) @@ -764,9 +725,7 @@ def repo_with_git_flow_angular_commits( @pytest.fixture def repo_with_git_flow_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() @@ -844,9 +803,7 @@ def repo_with_git_flow_emoji_commits( @pytest.fixture def repo_with_git_flow_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() @@ -924,9 +881,7 @@ def repo_with_git_flow_scipy_commits( @pytest.fixture def repo_with_git_flow_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_tag_parser() @@ -1009,17 +964,13 @@ def repo_with_git_flow_and_release_channels_angular_commits( git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, update_pyproject_toml: UpdatePyprojectTomlFn, - file_in_repo: str + file_in_repo: str, ) -> Repo: git_repo = git_repo_factory() use_angular_parser() update_pyproject_toml( "tool.semantic_release.branches.features", - { - "match": "feat.*", - "prerelease": True, - "prerelease_token": "alpha" - } + {"match": "feat.*", "prerelease": True, "prerelease_token": "alpha"}, ) add_text_to_file(git_repo, file_in_repo) @@ -1101,12 +1052,10 @@ def repo_with_git_flow_and_release_channels_angular_commits( @pytest.fixture def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() - use_angular_parser() # TODO: is this correct? + use_angular_parser() # TODO: is this correct? add_text_to_file(git_repo, file_in_repo) git_repo.index.add(("*", ".gitignore")) @@ -1187,9 +1136,7 @@ def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( @pytest.fixture def repo_with_git_flow_and_release_channels_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() @@ -1273,9 +1220,7 @@ def repo_with_git_flow_and_release_channels_emoji_commits( @pytest.fixture def repo_with_git_flow_and_release_channels_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() @@ -1359,9 +1304,7 @@ def repo_with_git_flow_and_release_channels_scipy_commits( @pytest.fixture def repo_with_git_flow_and_release_channels_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str + git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str ) -> Repo: git_repo = git_repo_factory() use_tag_parser() From 758e64975fe46b961809f35977574729b7c44271 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 3 Jan 2024 13:55:57 -0500 Subject: [PATCH 032/167] fix(cli-generate-config): ensure configuration types are always toml parsable (#785) --- .../cli/commands/generate_config.py | 4 +- tests/command_line/test_generate_config.py | 59 ++++++++++++------- .../unit/semantic_release/cli/test_config.py | 2 +- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/semantic_release/cli/commands/generate_config.py b/semantic_release/cli/commands/generate_config.py index 296d87e75..b571728c8 100644 --- a/semantic_release/cli/commands/generate_config.py +++ b/semantic_release/cli/commands/generate_config.py @@ -40,7 +40,9 @@ def generate_config(fmt: str = "toml", is_pyproject_toml: bool = False) -> None: semantic-release generate-config -f toml >> pyproject.toml """ - config = RawConfig().model_dump(exclude_none=True) + # due to possible IntEnum values (which are not supported by tomlkit.dumps, see sdispater/tomlkit#237), + # we must ensure the transformation of the model to a dict uses json serializable values + config = RawConfig().model_dump(mode="json", exclude_none=True) config_dct = {"semantic_release": config} if is_pyproject_toml and fmt == "toml": diff --git a/tests/command_line/test_generate_config.py b/tests/command_line/test_generate_config.py index 196714980..0ff0bf99a 100644 --- a/tests/command_line/test_generate_config.py +++ b/tests/command_line/test_generate_config.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +from typing import TYPE_CHECKING import pytest import tomlkit @@ -8,37 +9,53 @@ from semantic_release.cli.commands.generate_config import generate_config from semantic_release.cli.config import RawConfig +if TYPE_CHECKING: + from typing import Any + + from tests.command_line.conftest import CliRunner + + +@pytest.fixture +def raw_config_dict() -> dict[str, Any]: + return RawConfig().model_dump(mode="json", exclude_none=True) + @pytest.mark.parametrize("args", [(), ("--format", "toml"), ("--format", "TOML")]) -def test_generate_config_toml(cli_runner, args): +def test_generate_config_toml( + cli_runner: CliRunner, args: tuple[str], raw_config_dict: dict[str, Any] +): + expected_config_as_str = tomlkit.dumps( + {"semantic_release": raw_config_dict} + ).strip() + result = cli_runner.invoke(generate_config, args) + assert result.exit_code == 0 - assert ( - result.output.strip() - == tomlkit.dumps( - {"semantic_release": RawConfig().model_dump(exclude_none=True)} - ).strip() - ) + assert expected_config_as_str == result.output.strip() @pytest.mark.parametrize("args", [("--format", "json"), ("--format", "JSON")]) -def test_generate_config_json(cli_runner, args): +def test_generate_config_json( + cli_runner: CliRunner, args: tuple[str], raw_config_dict: dict[str, Any] +): + expected_config_as_str = json.dumps( + {"semantic_release": raw_config_dict}, indent=4 + ).strip() + result = cli_runner.invoke(generate_config, args) + assert result.exit_code == 0 - assert ( - result.output.strip() - == json.dumps( - {"semantic_release": RawConfig().model_dump(exclude_none=True)}, indent=4 - ).strip() - ) + assert expected_config_as_str == result.output.strip() -def test_generate_config_pyproject_toml(cli_runner): +def test_generate_config_pyproject_toml( + cli_runner: CliRunner, raw_config_dict: dict[str, Any] +): + expected_config_as_str = tomlkit.dumps( + {"tool": {"semantic_release": raw_config_dict}} + ).strip() + result = cli_runner.invoke(generate_config, ["--format", "toml", "--pyproject"]) + assert result.exit_code == 0 - assert ( - result.output.strip() - == tomlkit.dumps( - {"tool": {"semantic_release": RawConfig().model_dump(exclude_none=True)}} - ).strip() - ) + assert expected_config_as_str == result.output.strip() diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index 2e0133de4..ae1e93056 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -52,7 +52,7 @@ def test_invalid_hvcs_type(remote_config: dict[str, Any]): def test_default_toml_config_valid(example_project): default_config_file = example_project / "default.toml" default_config_file.write_text( - tomlkit.dumps(RawConfig().model_dump(exclude_none=True)) + tomlkit.dumps(RawConfig().model_dump(mode='json', exclude_none=True)) ) written = default_config_file.read_text(encoding="utf-8") From df1df0de8bc655cbf8f86ae52aff10efdc66e6d2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 3 Jan 2024 13:57:22 -0500 Subject: [PATCH 033/167] docs(configuration): change defaults definition of token default to table (#786) --- docs/configuration.rst | 16 ++++++++++------ docs/index.rst | 6 +++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 75fa0eacd..210c1c32a 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -579,12 +579,16 @@ environment variable if you wish. The default value for this setting depends on what you specify as -:ref:`remote.type `. If you set ``remote.type = "github"``, -the default value for ``remote.token`` will be ``{ env = "GH_TOKEN" }``; -if you set ``remote.type = "gitlab"``, the default value will be -``{ env = "GITLAB_TOKEN" }``; and if you set ``remote.type = "gitea"``, the -default value of ``remote.token`` will be ``{ env = "GITEA_TOKEN" }``. - +:ref:`remote.type `. Review the table below to see what the +default token value will be for each remote type. + +================ == ============================ +``remote.type`` Default ``remote.token`` +================ == ============================ +``"github"`` -> ``{ env = "GH_TOKEN" }`` +``"gitlab"`` -> ``{ env = "GITLAB_TOKEN" }`` +``"gitea"`` -> ``{ env = "GITEA_TOKEN" }`` +================ == ============================ **Default:** ``{ env = "" }``, where ```` depends on :ref:`remote.type ` as indicated above. diff --git a/docs/index.rst b/docs/index.rst index fbaf97ea2..158743ad3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -129,11 +129,11 @@ usage. This token should be stored in the ``GH_TOKEN`` environment variable To generate a token go to https://github.com/settings/tokens and click on *Personal access token*. -GitLab (``GL_TOKEN``) -""""""""""""""""""""" +GitLab (``GITLAB_TOKEN``) +""""""""""""""""""""""""" A personal access token from GitLab. This is used for authenticating when pushing -tags, publishing releases etc. This token should be stored in the ``GL_TOKEN`` +tags, publishing releases etc. This token should be stored in the ``GITLAB_TOKEN`` environment variable. Gitea (``GITEA_TOKEN``) From 011b0729cba3045b4e7291fd970cb17aad7bae60 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 3 Jan 2024 13:59:48 -0500 Subject: [PATCH 034/167] docs(contributing): add docs-build, testing conf, & build instructions (#787) --- CONTRIBUTING.rst | 58 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index c88d3cf6d..33fcc3297 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -29,19 +29,69 @@ that should result in a new release it will happen if the build is green. Development ~~~~~~~~~~~ -Install this module and the development dependencies:: +Install this module and the development dependencies - pip install -e ".[test,dev]" +.. code-block:: bash -And if you'd like to build the documentation locally:: + pip install -e ".[dev,mypy,test]" + +And if you'd like to build the documentation locally + +.. code-block:: bash pip install -e ".[docs]" + sphinx-autobuild --open-browser docs docs/_build/html Testing ~~~~~~~ To test your modifications locally: -:: +.. code-block:: bash + # Run type-checking, all tests across all supported Python versions tox + + # Run all tests for your current installed Python version (with full error output) + pytest -vv tests/ + +If you need to run tests in a debugger, such as VSCode, you will need to adjust +``pyproject.toml`` temporarily: + +.. code-block:: diff + + diff --git a/pyproject.toml b/pyproject.toml + + [tool.pytest.ini_options] + addopts = [ + + "-n0", + - "-nauto", + "-ra", + "--cache-clear", + - "--cov=semantic_release", + - "--cov-context=test", + - "--cov-report", + - "html:coverage-html", + - "--cov-report", + - "term", + ] + +.. note:: + + The ``-n0`` option disables ``xdist``'s parallel testing. The removal of the coverage options + is to avoid a bug in ``pytest-cov`` that prevents VSCode from stopping at the breakpoints. + +Building +~~~~~~~~ + +This project is designed to be versioned and built by itself using the ``tool.semantic_release`` +configuration in ``pyproject.toml``. The setting ``tool.semantic_release.build_command`` defines +the command to run to build the package. + +The following is a copy of the ``build_command`` setting which can be run manually to build the +package locally: + +.. code-block:: bash + + python -m pip install build~=0.10.0 + python -m build . From 06d5c610642f5a515317b9030368f279086696fc Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 3 Jan 2024 19:04:57 +0000 Subject: [PATCH 035/167] style: beautify 011b0729cba3045b4e7291fd970cb17aad7bae60 --- tests/unit/semantic_release/cli/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index ae1e93056..0f81aad5c 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -52,7 +52,7 @@ def test_invalid_hvcs_type(remote_config: dict[str, Any]): def test_default_toml_config_valid(example_project): default_config_file = example_project / "default.toml" default_config_file.write_text( - tomlkit.dumps(RawConfig().model_dump(mode='json', exclude_none=True)) + tomlkit.dumps(RawConfig().model_dump(mode="json", exclude_none=True)) ) written = default_config_file.read_text(encoding="utf-8") From bd50081afb9efa86f3d4de60c217b55398364c27 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Wed, 3 Jan 2024 19:07:09 +0000 Subject: [PATCH 036/167] 8.7.1 Automatically generated by python-semantic-release --- CHANGELOG.md | 25 +++++++++++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73222c09a..71b2f0463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ +## v8.7.1 (2024-01-03) + +### Documentation + +* docs(contributing): add docs-build, testing conf, & build instructions (#787) ([`011b072`](https://github.com/python-semantic-release/python-semantic-release/commit/011b0729cba3045b4e7291fd970cb17aad7bae60)) + +* docs(configuration): change defaults definition of token default to table (#786) ([`df1df0d`](https://github.com/python-semantic-release/python-semantic-release/commit/df1df0de8bc655cbf8f86ae52aff10efdc66e6d2)) + +* docs: add note on default envvar behaviour (#780) ([`0b07cae`](https://github.com/python-semantic-release/python-semantic-release/commit/0b07cae71915c5c82d7784898b44359249542a64)) + +### Fix + +* fix(cli-generate-config): ensure configuration types are always toml parsable (#785) ([`758e649`](https://github.com/python-semantic-release/python-semantic-release/commit/758e64975fe46b961809f35977574729b7c44271)) + +### Style + +* style: beautify 011b0729cba3045b4e7291fd970cb17aad7bae60 ([`06d5c61`](https://github.com/python-semantic-release/python-semantic-release/commit/06d5c610642f5a515317b9030368f279086696fc)) + +* style: beautify d6c4ae0db458f8108c88d75ac4e07079bc747d32 ([`253c99e`](https://github.com/python-semantic-release/python-semantic-release/commit/253c99e72c1f4ddefd806c87cae10d1b72ff461b)) + +### Test + +* test(infrastructure): refactor test fixtures & configuration for higher resiliency (#773) ([`d6c4ae0`](https://github.com/python-semantic-release/python-semantic-release/commit/d6c4ae0db458f8108c88d75ac4e07079bc747d32)) + + ## v8.7.0 (2023-12-22) ### Feature diff --git a/pyproject.toml b/pyproject.toml index 1b5e069e5..e45ecadb7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "8.7.0" +version = "8.7.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.7" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 29c107720..931879fdf 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "8.7.0" +__version__ = "8.7.1" def setup_hook(argv: list[str]) -> None: From 6835fcad54b452ac212ee132a8424453c1c1e150 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:48:10 +0000 Subject: [PATCH 037/167] build(deps-dev): bump ruff from 0.1.8 to 0.1.11 (#792) Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.8 to 0.1.11. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/v0.1.8...v0.1.11) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e45ecadb7..948f7052f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ test = [ "requests-mock>=1.10.0,<2", "types-pytest-lazy-fixture>=0.6.3.3", ] -dev = ["pre-commit", "tox", "ruff==0.1.8"] +dev = ["pre-commit", "tox", "ruff==0.1.11"] mypy = ["mypy", "types-requests"] [tool.pytest.ini_options] From c9556b0ca6df6a61e9ce909d18bc5be8b6154bf8 Mon Sep 17 00:00:00 2001 From: Bernard Cooke Date: Wed, 3 Jan 2024 20:27:20 +0000 Subject: [PATCH 038/167] fix(lint): correct linter errors --- CHANGELOG.md | 1294 +++++++++---------- semantic_release/cli/commands/version.py | 8 +- semantic_release/cli/config.py | 2 +- semantic_release/hvcs/_base.py | 2 +- semantic_release/hvcs/gitea.py | 2 +- semantic_release/hvcs/github.py | 2 +- semantic_release/hvcs/gitlab.py | 2 +- tests/fixtures/example_project.py | 22 +- tests/unit/semantic_release/test_helpers.py | 2 +- 9 files changed, 673 insertions(+), 663 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71b2f0463..9b18f6dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,55 +61,55 @@ Git remote url parsing now supports additional formats (ssh, https, file, git) ( * build(deps-dev): bump ruff from 0.1.7 to 0.1.8 (#775) -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.7 to 0.1.8. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.7...v0.1.8) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.7 to 0.1.8. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.7...v0.1.8) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`5efda8a`](https://github.com/python-semantic-release/python-semantic-release/commit/5efda8acfed938d3188cd55678ace20ecac7f798)) * build(deps-dev): bump ruff from 0.1.6 to 0.1.7 (#769) -* build(deps-dev): bump ruff from 0.1.6 to 0.1.7 - -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.6 to 0.1.7. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.6...v0.1.7) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> - -* ci: remove hardcoded ruff version in workflows - ---------- - -Signed-off-by: dependabot[bot] <support@github.com> -Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> +* build(deps-dev): bump ruff from 0.1.6 to 0.1.7 + +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.6 to 0.1.7. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.6...v0.1.7) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> + +* ci: remove hardcoded ruff version in workflows + +--------- + +Signed-off-by: dependabot[bot] <support@github.com> +Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> ([`c48c3b3`](https://github.com/python-semantic-release/python-semantic-release/commit/c48c3b370335931d63391d1a4f5802937deff178)) ### Fix * fix(cli): gracefully output configuration validation errors (#772) -* test(fixtures): update example project workflow & add config modifier - -* test(cli-main): add test for raw config validation error - +* test(fixtures): update example project workflow & add config modifier + +* test(cli-main): add test for raw config validation error + * fix(cli): gracefully output configuration validation errors ([`e8c9d51`](https://github.com/python-semantic-release/python-semantic-release/commit/e8c9d516c37466a5dce75a73766d5be0f9e74627)) ### Style @@ -125,26 +125,26 @@ Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> ([`c48c3b3`](htt * docs(configuration): adjust wording and improve clarity (#766) -* docs(configuration): fix typo in text - +* docs(configuration): fix typo in text + * docs(configuration): adjust wording and improve clarity ([`6b2fc8c`](https://github.com/python-semantic-release/python-semantic-release/commit/6b2fc8c156e122ee1b43fdb513b2dc3b8fd76724)) ### Fix * fix(config): gracefully fail when repo is in a detached HEAD state (#765) -* fix(config): cleanly handle repository in detached HEAD state - +* fix(config): cleanly handle repository in detached HEAD state + * test(cli-main): add detached head cli test ([`ac4f9aa`](https://github.com/python-semantic-release/python-semantic-release/commit/ac4f9aacb72c99f2479ae33369822faad011a824)) * fix(cmd-version): handle committing of git-ignored file gracefully (#764) -* fix(version): only commit non git-ignored files during version commit - -* test(version): set version file as ignored file - -Tweaks tests to use one committed change file and the version file -as an ignored change file. This allows us to verify that our commit +* fix(version): only commit non git-ignored files during version commit + +* test(version): set version file as ignored file + +Tweaks tests to use one committed change file and the version file +as an ignored change file. This allows us to verify that our commit mechanism does not crash if a file that is changed is ignored by user ([`ea89fa7`](https://github.com/python-semantic-release/python-semantic-release/commit/ea89fa72885e15da91687172355426a22c152513)) ### Style @@ -165,35 +165,35 @@ mechanism does not crash if a file that is changed is ignored by user ([`ea89fa7 * build(deps-dev): bump ruff from 0.1.2 to 0.1.6 (#757) -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.2 to 0.1.6. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.2...v0.1.6) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.2 to 0.1.6. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.2...v0.1.6) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`90db8f1`](https://github.com/python-semantic-release/python-semantic-release/commit/90db8f1bd8986eda1b913cd4bab5abd41192f01f)) * build(deps-dev): update python-gitlab requirement from <4,>=2 to >=2,<5 (#748) -Updates the requirements on [python-gitlab](https://github.com/python-gitlab/python-gitlab) to permit the latest version. -- [Release notes](https://github.com/python-gitlab/python-gitlab/releases) -- [Changelog](https://github.com/python-gitlab/python-gitlab/blob/main/CHANGELOG.md) -- [Commits](https://github.com/python-gitlab/python-gitlab/compare/v2.0.0...v4.1.1) - ---- -updated-dependencies: -- dependency-name: python-gitlab - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [python-gitlab](https://github.com/python-gitlab/python-gitlab) to permit the latest version. +- [Release notes](https://github.com/python-gitlab/python-gitlab/releases) +- [Changelog](https://github.com/python-gitlab/python-gitlab/blob/main/CHANGELOG.md) +- [Commits](https://github.com/python-gitlab/python-gitlab/compare/v2.0.0...v4.1.1) + +--- +updated-dependencies: +- dependency-name: python-gitlab + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`a176d62`](https://github.com/python-semantic-release/python-semantic-release/commit/a176d626f28ba68ae8a938b2f04f74da841a7eeb)) * build(deps-dev): bump ruff from 0.0.292 to 0.1.1 ([`9c5bbe0`](https://github.com/python-semantic-release/python-semantic-release/commit/9c5bbe0b0ef96e0fadae9e65918fc8939d0d3e60)) @@ -206,12 +206,12 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * feat(cmd-version): add `--tag/--no-tag` option to version command (#752) -* fix(version): separate push tags from commit push when not committing changes - -* feat(version): add `--no-tag` option to turn off tag creation - -* test(version): add test for `--tag` option & `--no-tag/commit` - +* fix(version): separate push tags from commit push when not committing changes + +* feat(version): add `--no-tag` option to turn off tag creation + +* test(version): add test for `--tag` option & `--no-tag/commit` + * docs(commands): update `version` subcommand options ([`de6b9ad`](https://github.com/python-semantic-release/python-semantic-release/commit/de6b9ad921e697b5ea2bb2ea8f180893cecca920)) ### Style @@ -286,19 +286,19 @@ GitHub.upload_asset now raises ValueError instead of requests.HTTPError ([`a13a6 * build(deps-dev): bump ruff from 0.0.286 to 0.0.290 (#713) -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.286 to 0.0.290. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.286...v0.0.290) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.286 to 0.0.290. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.286...v0.0.290) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`6b288f2`](https://github.com/python-semantic-release/python-semantic-release/commit/6b288f2033366a8a2d6e730938df3606fa5ca5a7)) ### Fix @@ -312,12 +312,12 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 (#704) -* build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 - -* build(deps-dev): relax importlib-resources requirement - ---------- - +* build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 + +* build(deps-dev): relax importlib-resources requirement + +--------- + Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`861fe01`](https://github.com/python-semantic-release/python-semantic-release/commit/861fe0119131f69761ae3e06ec46d62e526269d6)) ### Chore @@ -363,50 +363,50 @@ Co-authored-by: github-actions <action@github.com> ([`8a515ca`](https://gi * build(deps-dev): update pytest-xdist requirement (#677) -Updates the requirements on [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) to permit the latest version. -- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst) -- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.0.0...v3.3.1) - ---- -updated-dependencies: -- dependency-name: pytest-xdist - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) to permit the latest version. +- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst) +- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.0.0...v3.3.1) + +--- +updated-dependencies: +- dependency-name: pytest-xdist + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`9ec6321`](https://github.com/python-semantic-release/python-semantic-release/commit/9ec63215c8d60230ff90c06096d8f37956bd156a)) * build(deps-dev): update coverage[toml] requirement (#678) -Updates the requirements on [coverage[toml]](https://github.com/nedbat/coveragepy) to permit the latest version. -- [Release notes](https://github.com/nedbat/coveragepy/releases) -- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) -- [Commits](https://github.com/nedbat/coveragepy/compare/6.0...7.3.0) - ---- -updated-dependencies: -- dependency-name: coverage[toml] - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [coverage[toml]](https://github.com/nedbat/coveragepy) to permit the latest version. +- [Release notes](https://github.com/nedbat/coveragepy/releases) +- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) +- [Commits](https://github.com/nedbat/coveragepy/compare/6.0...7.3.0) + +--- +updated-dependencies: +- dependency-name: coverage[toml] + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`3561a97`](https://github.com/python-semantic-release/python-semantic-release/commit/3561a977fc56ec4bb12c9a894c0072cd8a3519fa)) * build(deps-dev): bump responses from 0.21.0 to 0.23.3 (#680) -Bumps [responses](https://github.com/getsentry/responses) from 0.21.0 to 0.23.3. -- [Release notes](https://github.com/getsentry/responses/releases) -- [Changelog](https://github.com/getsentry/responses/blob/master/CHANGES) -- [Commits](https://github.com/getsentry/responses/compare/0.21.0...0.23.3) - ---- -updated-dependencies: -- dependency-name: responses - dependency-type: direct:production - update-type: version-update:semver-minor -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [responses](https://github.com/getsentry/responses) from 0.21.0 to 0.23.3. +- [Release notes](https://github.com/getsentry/responses/releases) +- [Changelog](https://github.com/getsentry/responses/blob/master/CHANGES) +- [Commits](https://github.com/getsentry/responses/compare/0.21.0...0.23.3) + +--- +updated-dependencies: +- dependency-name: responses + dependency-type: direct:production + update-type: version-update:semver-minor +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`dd6946c`](https://github.com/python-semantic-release/python-semantic-release/commit/dd6946cd479f646f3af028b92ca83db8fc4c0c66)) ### Chore @@ -436,8 +436,8 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * fix: don't warn about vcs token if ignore_token_for_push is true. (#670) -* fix: don't warn about vcs token if ignore_token_for_push is true. - +* fix: don't warn about vcs token if ignore_token_for_push is true. + * docs: `password` should be `token`. ([`f1a54a6`](https://github.com/python-semantic-release/python-semantic-release/commit/f1a54a6c9a05b225b6474d50cd610eca19ec0c34)) ### Style @@ -503,184 +503,184 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * feat!: v8 (#619) -* feat!: 8.0.x (#538) - -Co-authored-by: Johan <johanhmr@gmail.com> -Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> - -* fix: correct Dockerfile CLI command and GHA fetch - -* fix: resolve branch checkout logic in GHA - -* fix: remove commit amending behaviour - -this was not working when there were no source code changes to be made, as it lead -to attempting to amend a HEAD commit that wasn't produced by PSR - -* 8.0.0-alpha.1 - -Automatically generated by python-semantic-release - -* fix: correct logic for generating release notes (#550) - -* fix: cleanup comments and unused logic - -* fix(action): mark container fs as safe for git to operate on - -* style: beautify 49080c510a68cccd2f6c7a8d540b483751901207 - -* fix(action): quotation for git config command - -* 8.0.0-alpha.2 - -Automatically generated by python-semantic-release - -* fix: resolve bug in changelog logic, enable upload to pypi - -* 8.0.0-alpha.3 - -Automatically generated by python-semantic-release - -* test: add tests for ReleaseHistory.release - -* fix: resolve loss of tag_format configuration - -* 8.0.0-alpha.4 - -Automatically generated by python-semantic-release - -* feat: various improvements - -* Added sorting to test parameterisation, so that pytest-xdist works again - dramatic speedup for testing -* Reworked the CI verification code so it's a bit prettier -* Added more testing for the version CLI command, and split some logic out of the command itself -* Removed a redundant double-regex match in VersionTranslator and Version, for some speedup - -* chore(test): proper env patching for tests in CI - -* style: beautify bcb27a4a8ce4789d083226f088c1810f45cd4c77 - -* refactor!: remove verify-ci command - -* 8.0.0-alpha.5 - -Automatically generated by python-semantic-release - -* fix(docs): fixup docs and remove reference to dist publication - -* feat!: remove publication of dists to artefact repository - -* feat: rename 'upload' configuration section to 'publish' - -* feat!: removed build status checking - -* feat: add GitHub Actions output - -* fix(action): remove default for 'force' - -* fix(ci): different workflow for v8 - -* fix(action): correct input parsing - -* fix: correct handling of build commands - -* feat: make it easier to access commit messages in ParsedCommits - -* fix: make additional attributes available for template authors - -* fix: add logging for token auth, use token for push - -* ci: add verbosity - -* fix: caching for repo owner and name - -* ci: contents permission for workflow - -* 8.0.0-alpha.6 - -Automatically generated by python-semantic-release - -* docs: update docs with additional required permissions - -* feat: add option to specify tag to publish to in publish command - -* feat: add Strict Mode - -* docs: convert to Furo theme - -* feat: add --skip-build option - -* 8.0.0-alpha.7 - -Automatically generated by python-semantic-release - -* test: separate command line tests by stdout and stderr - -* ci: pass tag output and conditionally execute publish steps - -* fix: correct assets type in configuration (#603) - -* change raw config assets type - -* fix: correct assets type-annotation for RuntimeContext - ---------- - -Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> - -* 8.0.0-alpha.8 - -Automatically generated by python-semantic-release - -* fix: pin Debian version in Dockerfile - -* feat: promote to rc - -* 8.0.0-rc.1 - -Automatically generated by python-semantic-release - -* ci: fix conditionals in workflow and update documentation - -* ci: correct conditionals - -* fix: only call Github Action output callback once defaults are set - -* 8.0.0-rc.2 - -Automatically generated by python-semantic-release - -* fix: create_or_update_release for Gitlab hvcs - -* ci: remove separate v8 workflow - -* chore: tweak issue templates - -* chore: bump docs dependencies - -* 8.0.0-rc.3 - -Automatically generated by python-semantic-release - -* fix(deps): add types-click, and downgrade sphinx/furo for 3.7 - -* 8.0.0-rc.4 - -Automatically generated by python-semantic-release - -* docs: fix typo (#623) - -* docs: correct typo in docs/changelog_templates.rst - -Co-authored-by: Micael Jarniac <micael@jarniac.com> - ---------- - -Co-authored-by: Johan <johanhmr@gmail.com> -Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> -Co-authored-by: semantic-release <semantic-release> -Co-authored-by: github-actions <action@github.com> -Co-authored-by: smeng9 <38666763+smeng9@users.noreply.github.com> +* feat!: 8.0.x (#538) + +Co-authored-by: Johan <johanhmr@gmail.com> +Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> + +* fix: correct Dockerfile CLI command and GHA fetch + +* fix: resolve branch checkout logic in GHA + +* fix: remove commit amending behaviour + +this was not working when there were no source code changes to be made, as it lead +to attempting to amend a HEAD commit that wasn't produced by PSR + +* 8.0.0-alpha.1 + +Automatically generated by python-semantic-release + +* fix: correct logic for generating release notes (#550) + +* fix: cleanup comments and unused logic + +* fix(action): mark container fs as safe for git to operate on + +* style: beautify 49080c510a68cccd2f6c7a8d540b483751901207 + +* fix(action): quotation for git config command + +* 8.0.0-alpha.2 + +Automatically generated by python-semantic-release + +* fix: resolve bug in changelog logic, enable upload to pypi + +* 8.0.0-alpha.3 + +Automatically generated by python-semantic-release + +* test: add tests for ReleaseHistory.release + +* fix: resolve loss of tag_format configuration + +* 8.0.0-alpha.4 + +Automatically generated by python-semantic-release + +* feat: various improvements + +* Added sorting to test parameterisation, so that pytest-xdist works again - dramatic speedup for testing +* Reworked the CI verification code so it's a bit prettier +* Added more testing for the version CLI command, and split some logic out of the command itself +* Removed a redundant double-regex match in VersionTranslator and Version, for some speedup + +* chore(test): proper env patching for tests in CI + +* style: beautify bcb27a4a8ce4789d083226f088c1810f45cd4c77 + +* refactor!: remove verify-ci command + +* 8.0.0-alpha.5 + +Automatically generated by python-semantic-release + +* fix(docs): fixup docs and remove reference to dist publication + +* feat!: remove publication of dists to artefact repository + +* feat: rename 'upload' configuration section to 'publish' + +* feat!: removed build status checking + +* feat: add GitHub Actions output + +* fix(action): remove default for 'force' + +* fix(ci): different workflow for v8 + +* fix(action): correct input parsing + +* fix: correct handling of build commands + +* feat: make it easier to access commit messages in ParsedCommits + +* fix: make additional attributes available for template authors + +* fix: add logging for token auth, use token for push + +* ci: add verbosity + +* fix: caching for repo owner and name + +* ci: contents permission for workflow + +* 8.0.0-alpha.6 + +Automatically generated by python-semantic-release + +* docs: update docs with additional required permissions + +* feat: add option to specify tag to publish to in publish command + +* feat: add Strict Mode + +* docs: convert to Furo theme + +* feat: add --skip-build option + +* 8.0.0-alpha.7 + +Automatically generated by python-semantic-release + +* test: separate command line tests by stdout and stderr + +* ci: pass tag output and conditionally execute publish steps + +* fix: correct assets type in configuration (#603) + +* change raw config assets type + +* fix: correct assets type-annotation for RuntimeContext + +--------- + +Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> + +* 8.0.0-alpha.8 + +Automatically generated by python-semantic-release + +* fix: pin Debian version in Dockerfile + +* feat: promote to rc + +* 8.0.0-rc.1 + +Automatically generated by python-semantic-release + +* ci: fix conditionals in workflow and update documentation + +* ci: correct conditionals + +* fix: only call Github Action output callback once defaults are set + +* 8.0.0-rc.2 + +Automatically generated by python-semantic-release + +* fix: create_or_update_release for Gitlab hvcs + +* ci: remove separate v8 workflow + +* chore: tweak issue templates + +* chore: bump docs dependencies + +* 8.0.0-rc.3 + +Automatically generated by python-semantic-release + +* fix(deps): add types-click, and downgrade sphinx/furo for 3.7 + +* 8.0.0-rc.4 + +Automatically generated by python-semantic-release + +* docs: fix typo (#623) + +* docs: correct typo in docs/changelog_templates.rst + +Co-authored-by: Micael Jarniac <micael@jarniac.com> + +--------- + +Co-authored-by: Johan <johanhmr@gmail.com> +Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> +Co-authored-by: semantic-release <semantic-release> +Co-authored-by: github-actions <action@github.com> +Co-authored-by: smeng9 <38666763+smeng9@users.noreply.github.com> Co-authored-by: Micael Jarniac <micael@jarniac.com> ([`ec30564`](https://github.com/python-semantic-release/python-semantic-release/commit/ec30564b4ec732c001d76d3c09ba033066d2b6fe)) @@ -704,11 +704,11 @@ Co-authored-by: Micael Jarniac <micael@jarniac.com> ([`ec30564`](https://g * fix: docker build fails installing git (#605) -git was installed from bullseye-backports, but base image is referencing latest python:3.10 -since bookworm was recently released, this now points at bookworm and installing the backport of git is actually trying to downgrade, resulting in this error: - -> E: Packages were downgraded and -y was used without --allow-downgrades. - +git was installed from bullseye-backports, but base image is referencing latest python:3.10 +since bookworm was recently released, this now points at bookworm and installing the backport of git is actually trying to downgrade, resulting in this error: + +> E: Packages were downgraded and -y was used without --allow-downgrades. + > ERROR: failed to solve: process "/bin/sh -c echo \"deb http://deb.debian.org/debian bullseye-backports main\" >> /etc/apt/sources.list; apt-get update; apt-get install -y git/bullseye-backports" did not complete successfully: exit code: 100 ([`9e3eb97`](https://github.com/python-semantic-release/python-semantic-release/commit/9e3eb979783bc39ca564c2967c6c77eecba682e6)) @@ -734,7 +734,7 @@ In #594, I missed that there are 2 places where the version header is formatted * fix: generate markdown linter compliant changelog headers & lists (#594) -Add an extra new line after each header and between sections to fix 2 markdownlint errors +Add an extra new line after each header and between sections to fix 2 markdownlint errors for changelogs generated by this package ([`9d9d403`](https://github.com/python-semantic-release/python-semantic-release/commit/9d9d40305c499c907335abe313e3ed122db0b154)) ### Style @@ -748,9 +748,9 @@ for changelogs generated by this package ([`9d9d403`](https://github.com/python- * feat: add option to only parse commits for current working directory (#509) -When running the application from a sub-directory in the VCS, the option -use_only_cwd_commits will filter out commits that does not changes the -current working directory, similar to running commands like +When running the application from a sub-directory in the VCS, the option +use_only_cwd_commits will filter out commits that does not changes the +current working directory, similar to running commands like `git log -- .` in a sub-directory. ([`cdf8116`](https://github.com/python-semantic-release/python-semantic-release/commit/cdf8116c1e415363b10a01f541873e04ad874220)) @@ -760,22 +760,22 @@ current working directory, similar to running commands like * docs: update broken badge and add links (#591) -The "Test Status" badge was updated to address a recent breaking change in the -GitHub actions API. All the badges updated to add links to the appropriate +The "Test Status" badge was updated to address a recent breaking change in the +GitHub actions API. All the badges updated to add links to the appropriate resources for end-user convenience. ([`0c23447`](https://github.com/python-semantic-release/python-semantic-release/commit/0c234475d27ad887b19170c82deb80293b3a95f1)) ### Fix * fix: update docs and default config for gitmoji changes (#590) -* fix: update docs and default config for gitmoji changes - -PR #582 updated to the latest Gitmojis release however the documentation and -default config options still referenced old and unsupported Gitmojis. - -* fix: update sphinx dep - -I could only build the documentation locally by updating Sphinx to the latest +* fix: update docs and default config for gitmoji changes + +PR #582 updated to the latest Gitmojis release however the documentation and +default config options still referenced old and unsupported Gitmojis. + +* fix: update sphinx dep + +I could only build the documentation locally by updating Sphinx to the latest 1.x version. ([`192da6e`](https://github.com/python-semantic-release/python-semantic-release/commit/192da6e1352298b48630423d50191070a1c5ab24)) @@ -802,16 +802,16 @@ In order to avoid 'Repository not found: relekang/python-semantic-release.&# * docs: spelling and grammar in `travis.rst` (#556) -- spelling -- subject-verb agreement -- remove verbiage - +- spelling +- subject-verb agreement +- remove verbiage + Signed-off-by: Vladislav Doster <mvdoster@gmail.com> ([`3a76e9d`](https://github.com/python-semantic-release/python-semantic-release/commit/3a76e9d7505c421009eb3e953c32cccac2e70e07)) * docs: grammar in `docs/troubleshooting.rst` (#557) -- change contraction to a possessive pronoun - +- change contraction to a possessive pronoun + Signed-off-by: Vladislav Doster <mvdoster@gmail.com> ([`bbe754a`](https://github.com/python-semantic-release/python-semantic-release/commit/bbe754a3db9ce7132749e7902fe118b52f48ee42)) ### Fix @@ -859,11 +859,11 @@ It is spamming to much. We can bring it back if we get the time to fix the spamm * ci: remove python3.6 from GHA, add python3.10 and python3.11 (#541) -* ci: remove python3.6 from GHA, add python3.10 and python3.11 - -GHA workflows are failing without this, due to -https://github.com/actions/setup-python/issues/544\#issuecomment-1332535877 - +* ci: remove python3.6 from GHA, add python3.10 and python3.11 + +GHA workflows are failing without this, due to +https://github.com/actions/setup-python/issues/544\#issuecomment-1332535877 + * fix: upgrade pytest ([`8e4aa0e`](https://github.com/python-semantic-release/python-semantic-release/commit/8e4aa0e30438291ade98604a18aeb372f0d0b52f)) ### Feature @@ -882,17 +882,17 @@ Co-authored-by: Kevin Watson <Kevmo92@users.noreply.github.com> ([`4664afe * fix: changelog release commit search logic (#530) -* Fixes changelog release commit search logic - -Running `semantic-release changelog` currently fails to identify "the last commit in [a] release" because the compared commit messages have superfluous whitespace. -Likely related to the issue causing: https://github.com/relekang/python-semantic-release/issues/490 - +* Fixes changelog release commit search logic + +Running `semantic-release changelog` currently fails to identify "the last commit in [a] release" because the compared commit messages have superfluous whitespace. +Likely related to the issue causing: https://github.com/relekang/python-semantic-release/issues/490 + * Removes a couple of extra `strip()`s. ([`efb3410`](https://github.com/python-semantic-release/python-semantic-release/commit/efb341036196c39b4694ca4bfa56c6b3e0827c6c)) * fix: bump Dockerfile to use Python 3.10 image (#536) -Fixes #533 - +Fixes #533 + Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`8f2185d`](https://github.com/python-semantic-release/python-semantic-release/commit/8f2185d570b3966b667ac591ae523812e9d2e00f)) * fix: fix mypy errors for publish ([`b40dd48`](https://github.com/python-semantic-release/python-semantic-release/commit/b40dd484387c1b3f78df53ee2d35e281e8e799c8)) @@ -918,10 +918,10 @@ Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`8f2185d`](http * ci: Update deprecated actions (#511) -* ci: update depreated actions - -* ci: replace deprecated set-output in workflow - +* ci: update depreated actions + +* ci: replace deprecated set-output in workflow + According to https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ ([`bb09233`](https://github.com/python-semantic-release/python-semantic-release/commit/bb09233b84d153a15784fdf68d7274c9d682c336)) ### Documentation @@ -1092,9 +1092,9 @@ Fixes #473 ([`0ece6f2`](https://github.com/python-semantic-release/python-semant * fix: allow changing prerelease tag using CLI flags (#466) -Delay construction of version and release patterns until runtime. -This will allow to use non-default prerelease tag. - +Delay construction of version and release patterns until runtime. +This will allow to use non-default prerelease tag. + Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`395bf4f`](https://github.com/python-semantic-release/python-semantic-release/commit/395bf4f2de73663c070f37cced85162d41934213)) @@ -1104,8 +1104,8 @@ Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`395bf4f`](http * fix(publish): get version bump for current release (#467) -Replicate the behavior of "version" command in version calculation. - +Replicate the behavior of "version" command in version calculation. + Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`dd26888`](https://github.com/python-semantic-release/python-semantic-release/commit/dd26888a923b2f480303c19f1916647de48b02bf)) * fix: add packaging module requirement (#469) ([`b99c9fa`](https://github.com/python-semantic-release/python-semantic-release/commit/b99c9fa88dc25e5ceacb131cd93d9079c4fb2c86)) @@ -1179,33 +1179,33 @@ Co-authored-by: Sebastian Seith <sebastian@vermill.io> ([`da0606f`](https: * ci: adjust actions test phase to use fetch-depth: 0 to fix ci tests (#446) -Co-authored-by: Sebastian Seith <sebastian@vermill.io> +Co-authored-by: Sebastian Seith <sebastian@vermill.io> Co-authored-by: github-actions <action@github.com> ([`3329eef`](https://github.com/python-semantic-release/python-semantic-release/commit/3329eeffb077f628e4a965bc7fd922d09d6b63da)) ### Feature * feat: allow using ssh-key to push version while using token to publish to hvcs (#419) -* feat(config): add ignore_token_for_push param - -Add ignore_token_for_push parameter that allows using the underlying -git authentication mechanism for pushing a new version commit and tags -while also using an specified token to upload dists - -* test(config): add test for ignore_token_for_push - -Test push_new_version with token while ignore_token_for_push is True -and False - -* docs: add documentation for ignore_token_for_push - -* fix(test): override GITHUB_ACTOR env - -push_new_version is using GITHUB_ACTOR env var but we did not -contemplate in our new tests that actually Github actions running the -tests will populate that var and change the test outcome - -Now we control the value of that env var and test for it being present +* feat(config): add ignore_token_for_push param + +Add ignore_token_for_push parameter that allows using the underlying +git authentication mechanism for pushing a new version commit and tags +while also using an specified token to upload dists + +* test(config): add test for ignore_token_for_push + +Test push_new_version with token while ignore_token_for_push is True +and False + +* docs: add documentation for ignore_token_for_push + +* fix(test): override GITHUB_ACTOR env + +push_new_version is using GITHUB_ACTOR env var but we did not +contemplate in our new tests that actually Github actions running the +tests will populate that var and change the test outcome + +Now we control the value of that env var and test for it being present or not ([`7b2dffa`](https://github.com/python-semantic-release/python-semantic-release/commit/7b2dffadf43c77d5e0eea307aefcee5c7744df5c)) ### Fix @@ -1243,16 +1243,16 @@ Fixes #354 ([`cf74339`](https://github.com/python-semantic-release/python-semant * chore(dependencies): unpin tomlkit dependency (#429) -- tests for a tomlkit regression don't fail anymore with newer tomlkit -- keep comment in setup.py about tomlkit being pinned at some point in time - +- tests for a tomlkit regression don't fail anymore with newer tomlkit +- keep comment in setup.py about tomlkit being pinned at some point in time + refs #336 ([`8515879`](https://github.com/python-semantic-release/python-semantic-release/commit/85158798ca438c1dafc84036d13c2988c934f02f)) ### Fix * fix(prerelase): pass prerelease option to get_current_version (#432) -The `get_current_version` function accepts a `prerelease` argument which +The `get_current_version` function accepts a `prerelease` argument which was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python-semantic-release/commit/aabab0b7ce647d25e0c78ae6566f1132ece9fcb9)) ### Style @@ -1266,7 +1266,7 @@ was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python * chore(dependencies): extend allowed version range for python-gitlab (#417) -* chore(dependencies): extend allowed version range for python-gitlab +* chore(dependencies): extend allowed version range for python-gitlab * fix(type): ignore mypy errors for dynamic RESTObject ([`8ee4d4b`](https://github.com/python-semantic-release/python-semantic-release/commit/8ee4d4b8dabfa5c6cd2aa6180d4a8da8f3c9554c)) ### Feature @@ -1280,18 +1280,18 @@ was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python * feat: add prerelease functionality (#413) -* chore: add initial todos -* feat: add prerelease tag option -* feat: add prerelease cli flag -* feat: omit_pattern for previouse and current version getters -* feat: print_version with prerelease bump -* feat: make print_version prerelease ready -* feat: move prerelease determination to get_new_version -* test: improve get_last_version test -* docs: added basic infos about prereleases -* feat: add prerelease flag to version and publish -* feat: remove leftover todos - +* chore: add initial todos +* feat: add prerelease tag option +* feat: add prerelease cli flag +* feat: omit_pattern for previouse and current version getters +* feat: print_version with prerelease bump +* feat: make print_version prerelease ready +* feat: move prerelease determination to get_new_version +* test: improve get_last_version test +* docs: added basic infos about prereleases +* feat: add prerelease flag to version and publish +* feat: remove leftover todos + Co-authored-by: Mario Jäckle <m.jaeckle@careerpartner.eu> ([`7064265`](https://github.com/python-semantic-release/python-semantic-release/commit/7064265627a2aba09caa2873d823b594e0e23e77)) ### Style @@ -1312,7 +1312,7 @@ Co-authored-by: Mario Jäckle <m.jaeckle@careerpartner.eu> ([`7064265`](ht * fix(gitea): build status and asset upload (#420) -* fix(gitea): handle list build status response +* fix(gitea): handle list build status response * fix(gitea): use form-data for upload_asset ([`57db81f`](https://github.com/python-semantic-release/python-semantic-release/commit/57db81f4c6b96da8259e3bad9137eaccbcd10f6e)) ### Style @@ -1444,10 +1444,10 @@ executable. No-op mode now respected by artifact upload. ([`cfb20af`](https://gi * feat: allow custom environment variable names (#392) -* GH_TOKEN can now be customized by setting github_token_var -* GL_TOKEN can now be customized by setting gitlab_token_var -* PYPI_PASSWORD can now be customized by setting pypi_pass_var -* PYPI_TOKEN can now be customized by setting pypi_token_var +* GH_TOKEN can now be customized by setting github_token_var +* GL_TOKEN can now be customized by setting gitlab_token_var +* PYPI_PASSWORD can now be customized by setting pypi_pass_var +* PYPI_TOKEN can now be customized by setting pypi_token_var * PYPI_USERNAME can now be customized by setting pypi_user_var ([`372cda3`](https://github.com/python-semantic-release/python-semantic-release/commit/372cda3497f16ead2209e6e1377d38f497144883)) * feat: use gitlab-ci or github actions env vars @@ -1515,8 +1515,8 @@ https://github.com/relekang/python-semantic-release/issues/391#issuecomment-9506 * feat: custom git tag format support (#373) -* feat: custom git tag format support -* test: add git tag format check +* feat: custom git tag format support +* test: add git tag format check * docs: add tag_format config option ([`1d76632`](https://github.com/python-semantic-release/python-semantic-release/commit/1d76632043bf0b6076d214a63c92013624f4b95e)) @@ -1562,9 +1562,9 @@ Co-authored-by: Laercio Barbosa <laercio.barbosa@scania.com> ([`a275a7a`]( * chore: bump responses to latest version (#343) -The current version has a Deprecation warning: - -inspect.getargspec() is deprecated since Python 3.0, +The current version has a Deprecation warning: + +inspect.getargspec() is deprecated since Python 3.0, use inspect.signature() or inspect.getfullargspec() ([`e953157`](https://github.com/python-semantic-release/python-semantic-release/commit/e953157125f4528759453218f75b6e51cafd2cc3)) ### Ci @@ -1747,23 +1747,23 @@ Fixes #329 ([`abfacc4`](https://github.com/python-semantic-release/python-semant * docs: correct casing on proper nouns (#320) -* docs: correcting Semantic Versioning casing - -Semantic Versioning is the name of the specification. -Therefore it is a proper noun. -This patch corrects the incorrect casing for Semantic Versioning. - -* docs: correcting Python casing - +* docs: correcting Semantic Versioning casing + +Semantic Versioning is the name of the specification. +Therefore it is a proper noun. +This patch corrects the incorrect casing for Semantic Versioning. + +* docs: correcting Python casing + This patch corrects the incorrect casing for Python. ([`d51b999`](https://github.com/python-semantic-release/python-semantic-release/commit/d51b999a245a4e56ff7a09d0495c75336f2f150d)) ### Feature * feat(checks): add support for Jenkins CI (#322) -Includes a ci check handler to verify jenkins. -Unlike other ci systems jenkins doesn't generally prefix things with -`JENKINS` or simply inject `JENKINS=true` Really the only thing that is +Includes a ci check handler to verify jenkins. +Unlike other ci systems jenkins doesn't generally prefix things with +`JENKINS` or simply inject `JENKINS=true` Really the only thing that is immediately identifiable is `JENKINS_URL` ([`3e99855`](https://github.com/python-semantic-release/python-semantic-release/commit/3e99855c6bc72b3e9a572c58cc14e82ddeebfff8)) ### Style @@ -1781,10 +1781,10 @@ immediately identifiable is `JENKINS_URL` ([`3e99855`](https://github.com/python * fix: fix crash when TOML has no PSR section (#319) -* test: reproduce issue with TOML without PSR section - -* fix: crash when TOML has no PSR section - +* test: reproduce issue with TOML without PSR section + +* fix: crash when TOML has no PSR section + * chore: remove unused imports ([`5f8ab99`](https://github.com/python-semantic-release/python-semantic-release/commit/5f8ab99bf7254508f4b38fcddef2bdde8dd15a4c)) @@ -1807,26 +1807,26 @@ Fixes #306 ([`1a85af4`](https://github.com/python-semantic-release/python-semant * feat: support toml files for version declaration (#307) -This introduce a new `version_toml` configuration property that behaves -like `version_pattern` and `version_variable`. - -For poetry support, user should now set `version_toml = pyproject.toml:tool.poetry.version`. - -This introduce an ABC class, `VersionDeclaration`, that -can be implemented to add other version declarations with ease. - -`toml` dependency has been replaced by `tomlkit`, as this is used -the library used by poetry to generate the `pyproject.toml` file, and -is able to keep the ordering of data defined in the file. - -Existing `VersionPattern` class has been renamed to -`PatternVersionDeclaration` and now implements `VersionDeclaration`. - -`load_version_patterns()` function has been renamed to -`load_version_declarations()` and now return a list of -`VersionDeclaration` implementations. - -Close #245 +This introduce a new `version_toml` configuration property that behaves +like `version_pattern` and `version_variable`. + +For poetry support, user should now set `version_toml = pyproject.toml:tool.poetry.version`. + +This introduce an ABC class, `VersionDeclaration`, that +can be implemented to add other version declarations with ease. + +`toml` dependency has been replaced by `tomlkit`, as this is used +the library used by poetry to generate the `pyproject.toml` file, and +is able to keep the ordering of data defined in the file. + +Existing `VersionPattern` class has been renamed to +`PatternVersionDeclaration` and now implements `VersionDeclaration`. + +`load_version_patterns()` function has been renamed to +`load_version_declarations()` and now return a list of +`VersionDeclaration` implementations. + +Close #245 Close #275 ([`9b62a7e`](https://github.com/python-semantic-release/python-semantic-release/commit/9b62a7e377378667e716384684a47cdf392093fa)) ### Style @@ -1846,8 +1846,8 @@ Fixes #311 ([`e2d8e47`](https://github.com/python-semantic-release/python-semant * feat(github): retry GitHub API requests on failure (#314) -* refactor(github): use requests.Session to call raise_for_status - +* refactor(github): use requests.Session to call raise_for_status + * fix(github): add retries to github API requests ([`ac241ed`](https://github.com/python-semantic-release/python-semantic-release/commit/ac241edf4de39f4fc0ff561a749fa85caaf9e2ae)) ### Style @@ -1941,10 +1941,10 @@ Fixes: #277 ([`ab3061a`](https://github.com/python-semantic-release/python-seman * fix(history): coerce version to string (#298) -The changes in #297 mistakenly omitted coercing the return value to a -string. This resulted in errors like: -"can only concatenate str (not "VersionInfo") to str" - +The changes in #297 mistakenly omitted coercing the return value to a +string. This resulted in errors like: +"can only concatenate str (not "VersionInfo") to str" + Add test case asserting it's type str ([`d4cdc3d`](https://github.com/python-semantic-release/python-semantic-release/commit/d4cdc3d3cd2d93f2a78f485e3ea107ac816c7d00)) * fix(history): require semver >= 2.10 @@ -2020,8 +2020,8 @@ the end of a PR that is merged via rebase merge or merge commit. ([`93e48c9`](ht * feat(changelog): add PR links in markdown (#282) -GitHub release notes automagically link to the PR, but changelog -markdown doesn't. Replace a PR number at the end of a message +GitHub release notes automagically link to the PR, but changelog +markdown doesn't. Replace a PR number at the end of a message with a markdown link. ([`0448f6c`](https://github.com/python-semantic-release/python-semantic-release/commit/0448f6c350bbbf239a81fe13dc5f45761efa7673)) ### Style @@ -2045,7 +2045,7 @@ To control if bump major or not when current major version is zero. ([`d324154`] * refactor(history): move changelog_scope default (#284) -* Move the default for changelog_scope from inline to defaults.cfg. +* Move the default for changelog_scope from inline to defaults.cfg. * Add missing header in docs. ([`b7e1376`](https://github.com/python-semantic-release/python-semantic-release/commit/b7e1376ee1688e5e6dcc069ce623f49e3a389052)) ### Style @@ -2061,13 +2061,13 @@ A few settings were not in alphabetical order. ([`60a3535`](https://github.com/p * feat(logs): include scope in changelogs (#281) -When the scope is set, include it in changelogs, e.g. -"feat(x): some description" becomes "**x**: some description". -This is similar to how the Node semantic release (and -conventional-changelog-generator) generates changelogs. -If scope is not given, it's omitted. - -Add a new config parameter changelog_scope to disable this behavior when +When the scope is set, include it in changelogs, e.g. +"feat(x): some description" becomes "**x**: some description". +This is similar to how the Node semantic release (and +conventional-changelog-generator) generates changelogs. +If scope is not given, it's omitted. + +Add a new config parameter changelog_scope to disable this behavior when set to 'False' ([`21c96b6`](https://github.com/python-semantic-release/python-semantic-release/commit/21c96b688cc44cc6f45af962ffe6d1f759783f37)) ### Style @@ -2113,11 +2113,11 @@ Fixes #260 ([`7cacca1`](https://github.com/python-semantic-release/python-semant * ci: check commit logs with commitlint (#263) -The contributing guide says that the project should itself follow the -Angular commit convention, but there is nothing to enforce it AFAIK. - -I had a similar problem on a project where I'm using -`python-semantic-release` and I've added a Github action to +The contributing guide says that the project should itself follow the +Angular commit convention, but there is nothing to enforce it AFAIK. + +I had a similar problem on a project where I'm using +`python-semantic-release` and I've added a Github action to test it on CI, you might find it useful too. ([`016fde6`](https://github.com/python-semantic-release/python-semantic-release/commit/016fde683924d380d25579bd0cff0c7f8b7b2240)) ### Documentation @@ -2142,7 +2142,7 @@ the method above. ([`5a5e2cf`](https://github.com/python-semantic-release/python * fix: add required to inputs in action metadata (#264) -According to the documentation, `inputs.<input_id>.required` is a +According to the documentation, `inputs.<input_id>.required` is a required field. ([`e76b255`](https://github.com/python-semantic-release/python-semantic-release/commit/e76b255cf7d3d156e3314fc28c54d63fa126e973)) @@ -2199,8 +2199,8 @@ Fixes #250 ([`87e2bb8`](https://github.com/python-semantic-release/python-semant * docs: give example of multiple build commands (#248) -I had a little trouble figuring out how to use a non-setup.py build -command, so I thought it would be helpful to update the docs with an +I had a little trouble figuring out how to use a non-setup.py build +command, so I thought it would be helpful to update the docs with an example of how to do this. ([`65f1ffc`](https://github.com/python-semantic-release/python-semantic-release/commit/65f1ffcc6cac3bf382f4b821ff2be59d04f9f867)) ### Fix @@ -2218,13 +2218,13 @@ example of how to do this. ([`65f1ffc`](https://github.com/python-semantic-relea * feat: bump versions in multiple files (#246) -- Add the `version_pattern` setting, which allows version numbers to be - identified using arbitrary regular expressions. -- Refactor the config system to allow non-string data types to be - specified in `pyproject.toml`. -- Multiple files can now be specified by setting `version_variable` or - `version_pattern` to a list in `pyproject.toml`. - +- Add the `version_pattern` setting, which allows version numbers to be + identified using arbitrary regular expressions. +- Refactor the config system to allow non-string data types to be + specified in `pyproject.toml`. +- Multiple files can now be specified by setting `version_variable` or + `version_pattern` to a list in `pyproject.toml`. + Fixes #175 ([`0ba2c47`](https://github.com/python-semantic-release/python-semantic-release/commit/0ba2c473c6e44cc326b3299b6ea3ddde833bdb37)) ### Style @@ -2245,9 +2245,9 @@ Fixes #175 ([`0ba2c47`](https://github.com/python-semantic-release/python-semant * feat(changelog): add changelog_table component (#242) -Add an alternative changelog component which displays each section as a -row in a table. - +Add an alternative changelog component which displays each section as a +row in a table. + Fixes #237 ([`fe6a7e7`](https://github.com/python-semantic-release/python-semantic-release/commit/fe6a7e7fa014ffb827a1430dbcc10d1fc84c886b)) ### Style @@ -2265,25 +2265,25 @@ Fixes #237 ([`fe6a7e7`](https://github.com/python-semantic-release/python-semant * feat(changelog): add changelog components (#240) -* feat(changelog): add changelog components - -Add the ability to configure sections of the changelog using a -`changelog_components` option. Component outputs are separated by a blank -line and appear in the same order as they were configured. - -It is possible to create your own custom components. Each component is a -function which returns either some text to be added, or None in which case it -will be skipped. - -BREAKING CHANGE: The `compare_url` option has been removed in favor of using -`changelog_components`. This functionality is now available as the -`semantic_release.changelog.compare_url` component. - -* docs: add documentation for changelog_components - -* feat: pass changelog_sections to components - -Changelog components may now receive the value of `changelog_sections`, +* feat(changelog): add changelog components + +Add the ability to configure sections of the changelog using a +`changelog_components` option. Component outputs are separated by a blank +line and appear in the same order as they were configured. + +It is possible to create your own custom components. Each component is a +function which returns either some text to be added, or None in which case it +will be skipped. + +BREAKING CHANGE: The `compare_url` option has been removed in favor of using +`changelog_components`. This functionality is now available as the +`semantic_release.changelog.compare_url` component. + +* docs: add documentation for changelog_components + +* feat: pass changelog_sections to components + +Changelog components may now receive the value of `changelog_sections`, split and ready to use. ([`3e17a98`](https://github.com/python-semantic-release/python-semantic-release/commit/3e17a98d7fa8468868a87e62651ac2c010067711)) ### Style @@ -2310,30 +2310,30 @@ Fixes #239 ([`34acbbc`](https://github.com/python-semantic-release/python-semant * feat(history): create emoji parser (#238) -Add a commit parser which uses emojis from https://gitmoji.carloscuesta.me/ -to determine the type of change. - -* fix: add emojis to default changelog_sections - -* fix: include all parsed types in changelog - -This allows emojis to appear in the changelog, as well as configuring -other types to appear with the Angular parser (I remember someone asking -for that feature a while ago). All filtering is now done in the -markdown_changelog function. - -* refactor(history): get breaking changes in parser - -Move the task of detecting breaking change descriptions into the commit -parser function, instead of during changelog generation. - -This has allowed the emoji parser to also return the regular descriptions as -breaking change descriptions for commits with :boom:. - -BREAKING CHANGE: Custom commit parser functions are now required to pass -a fifth argument to `ParsedCommit`, which is a list of breaking change -descriptions. - +Add a commit parser which uses emojis from https://gitmoji.carloscuesta.me/ +to determine the type of change. + +* fix: add emojis to default changelog_sections + +* fix: include all parsed types in changelog + +This allows emojis to appear in the changelog, as well as configuring +other types to appear with the Angular parser (I remember someone asking +for that feature a while ago). All filtering is now done in the +markdown_changelog function. + +* refactor(history): get breaking changes in parser + +Move the task of detecting breaking change descriptions into the commit +parser function, instead of during changelog generation. + +This has allowed the emoji parser to also return the regular descriptions as +breaking change descriptions for commits with :boom:. + +BREAKING CHANGE: Custom commit parser functions are now required to pass +a fifth argument to `ParsedCommit`, which is a list of breaking change +descriptions. + * docs: add documentation for emoji parser ([`2e1c50a`](https://github.com/python-semantic-release/python-semantic-release/commit/2e1c50a865628b372f48945a039a3edb38a7cdf0)) ### Style @@ -2407,12 +2407,12 @@ Any issues which are labelled as a question will be closed after two weeks of in * ci: pass SHA from beautify to release -Checkout the current SHA from the end of the beautify job for releasing, -instead of master. This will either be the same as the commit we are -running for, or the SHA of a style commit. This prevents releasing of +Checkout the current SHA from the end of the beautify job for releasing, +instead of master. This will either be the same as the commit we are +running for, or the SHA of a style commit. This prevents releasing of untested code. -See +See https://github.community/t5/GitHub-Actions/Checkout-commit-pushed-by-previous-job/m-p/55847#M9670 ([`76e34b6`](https://github.com/python-semantic-release/python-semantic-release/commit/76e34b6b52b8019e87eaddf295d0781b6aa51541)) ### Documentation @@ -2490,15 +2490,15 @@ This was missed in 213530fb0c914e274b81d1dacf38ea7322b5b91f ([`3084249`](https:/ * refactor(debug): use logging and click_log instead of ndebug -BREAKING CHANGE: `DEBUG="*"` no longer has an effect, instead use +BREAKING CHANGE: `DEBUG="*"` no longer has an effect, instead use `--verbosity DEBUG`. ([`15b1f65`](https://github.com/python-semantic-release/python-semantic-release/commit/15b1f650f29761e1ab2a91b767cbff79b2057a4c)) ### Build * build(pip): store requirements in setup.py -Remove the requirements directory and instead store all required -libraries directly inside setup.py. Development, testing and docs +Remove the requirements directory and instead store all required +libraries directly inside setup.py. Development, testing and docs dependencies are included as extras. ([`401468f`](https://github.com/python-semantic-release/python-semantic-release/commit/401468f312cf4f3b52006c68c58c4645b5e19802)) ### Chore @@ -2511,12 +2511,12 @@ Allow mypy and coverage to run on any Python version. ([`28feba6`](https://githu * ci: always checkout most recent commit to release -This should pull a beautify commit if one has been created, allowing the +This should pull a beautify commit if one has been created, allowing the new version to be pushed. ([`6c98aab`](https://github.com/python-semantic-release/python-semantic-release/commit/6c98aab932724e3aab08e68b75439bc8c31bd877)) * ci: cache testing dependencies -This should help improve the speed of the testing workflow by caching +This should help improve the speed of the testing workflow by caching downloaded dependencies. ([`4f53e35`](https://github.com/python-semantic-release/python-semantic-release/commit/4f53e351960a6b658f50265384c9e8f678718f68)) * ci: move beautification to separate workflow @@ -2533,7 +2533,7 @@ Run isort and Black on pushes to master. Any edits made are committed. isort and * docs: include README.rst in index.rst -These files were very similar so it makes sense to simply include one +These files were very similar so it makes sense to simply include one inside the other. ([`8673a9d`](https://github.com/python-semantic-release/python-semantic-release/commit/8673a9d92a9bf348bb3409e002a830741396c8ca)) * docs: rewrite README.rst ([`e049772`](https://github.com/python-semantic-release/python-semantic-release/commit/e049772cf14cdd49538cf357db467f0bf3fe9587)) @@ -2563,12 +2563,12 @@ inside the other. ([`8673a9d`](https://github.com/python-semantic-release/python * ci: fetch full history in release job -I didn't realise that actions/checkout@v2 only fetches 1 commit by +I didn't realise that actions/checkout@v2 only fetches 1 commit by default. ([`a02a9b7`](https://github.com/python-semantic-release/python-semantic-release/commit/a02a9b7e34d8e7f8bb3b9c8aa1b5e1ef8bdd406c)) * ci: run tests on pull_request -The tests didn't run for #211 which caused a flake8 failure to be +The tests didn't run for #211 which caused a flake8 failure to be missed. ([`32fd77e`](https://github.com/python-semantic-release/python-semantic-release/commit/32fd77ed835bcfc943abeacec4e327df045b2ec9)) * ci: run tests on GitHub Actions ([`39ff283`](https://github.com/python-semantic-release/python-semantic-release/commit/39ff283312a0c686bfc5be71e1da9b6456652d95)) @@ -2591,7 +2591,7 @@ Automatically create pages in the API docs section using sphinx-autodoc. This is * style: fix styling from 2997908 -These code style problems were introduced because tests didn't run on +These code style problems were introduced because tests didn't run on #211. ([`172391e`](https://github.com/python-semantic-release/python-semantic-release/commit/172391ec5b5e490081b9b0ea58a94dfd5be33937)) @@ -2620,31 +2620,31 @@ These code style problems were introduced because tests didn't run on * fix: Bump dependencies and fix Windows issues on Development (#173) -* Bump dependencies and fix windows issues - -* Correctly pass temp dir to test settings - -* Remove print call on test settings - -* chore: remove py34 and py35 classifiers - -* chore: bump twine, requests and python-gitlab - -* chore: update tox config to be more granular - -* fix: missing mime types on Windows - -* chore: bump circleCI and tox python to 3.8 - -* chore: remove py36 from tox envlist - +* Bump dependencies and fix windows issues + +* Correctly pass temp dir to test settings + +* Remove print call on test settings + +* chore: remove py34 and py35 classifiers + +* chore: bump twine, requests and python-gitlab + +* chore: update tox config to be more granular + +* fix: missing mime types on Windows + +* chore: bump circleCI and tox python to 3.8 + +* chore: remove py36 from tox envlist + * chore: isort errors ([`0a6f8c3`](https://github.com/python-semantic-release/python-semantic-release/commit/0a6f8c3842b05f5f424dad5ce1fa5e3823c7e688)) ### Refactor * refactor(history): use a named tuple for parsed commits -This improves readability as we can use attributes such as 'bump' and +This improves readability as we can use attributes such as 'bump' and 'descriptions' instead of confusing numeric indices. ([`bff40d5`](https://github.com/python-semantic-release/python-semantic-release/commit/bff40d53174ffe27451d82132c31b112c7bee9fd)) @@ -2683,10 +2683,10 @@ will get the version they specify. ([`123984d`](https://github.com/python-semant * feat(build): allow config setting for build command (#195) -* feat(build): allow config setting for build command - -BREAKING CHANGE: Previously the build_commands configuration variable set the types of bundles sent to `python setup.py`. It has been replaced by the configuration variable `build_command` which takes the full command e.g. `python setup.py sdist` or `poetry build`. - +* feat(build): allow config setting for build command + +BREAKING CHANGE: Previously the build_commands configuration variable set the types of bundles sent to `python setup.py`. It has been replaced by the configuration variable `build_command` which takes the full command e.g. `python setup.py sdist` or `poetry build`. + Closes #188 ([`740f4bd`](https://github.com/python-semantic-release/python-semantic-release/commit/740f4bdb26569362acfc80f7e862fc2c750a46dd)) ### Fix @@ -2784,46 +2784,46 @@ Fixes #181 ([`0fddbe2`](https://github.com/python-semantic-release/python-semant * feat: Upload distribution files to GitHub Releases (#177) -* refactor(github): create upload_asset function - -Create a function to call the asset upload API. This will soon be used -to upload assets specified by the user. - -* refactor(github): infer Content-Type from file extension - -Infer the Content-Type header based on the file extension instead of setting it manually. - -* refactor(pypi): move building of dists to cli.py - -Refactor to have the building/removal of distributions in cli.py instead -of within the upload_to_pypi function. This makes way for uploading to -other locations, such as GitHub Releases, too. - -* feat(github): upload dists to release - -Upload Python wheels to the GitHub release. Configured with the option -upload_to_release, on by default if using GitHub. - -* docs: document upload_to_release config option - -* test(github): add tests for Github.upload_dists - -* fix(github): fix upload of .whl files - -Fix uploading of .whl files due to a missing MIME type (defined custom type as application/x-wheel+zip). Additionally, continue with other uploads even if one fails. - -* refactor(cli): additional output during publish - -Add some additional output during the publish command. - -* refactor(github): move api calls to separate methods - -Move each type of GitHub API request into its own method to improve readability. - -Re-implementation of #172 - -* fix: post changelog after PyPI upload - +* refactor(github): create upload_asset function + +Create a function to call the asset upload API. This will soon be used +to upload assets specified by the user. + +* refactor(github): infer Content-Type from file extension + +Infer the Content-Type header based on the file extension instead of setting it manually. + +* refactor(pypi): move building of dists to cli.py + +Refactor to have the building/removal of distributions in cli.py instead +of within the upload_to_pypi function. This makes way for uploading to +other locations, such as GitHub Releases, too. + +* feat(github): upload dists to release + +Upload Python wheels to the GitHub release. Configured with the option +upload_to_release, on by default if using GitHub. + +* docs: document upload_to_release config option + +* test(github): add tests for Github.upload_dists + +* fix(github): fix upload of .whl files + +Fix uploading of .whl files due to a missing MIME type (defined custom type as application/x-wheel+zip). Additionally, continue with other uploads even if one fails. + +* refactor(cli): additional output during publish + +Add some additional output during the publish command. + +* refactor(github): move api calls to separate methods + +Move each type of GitHub API request into its own method to improve readability. + +Re-implementation of #172 + +* fix: post changelog after PyPI upload + Post the changelog in-between uploading to PyPI and uploading to GitHub Releases. This is so that if the PyPI upload fails, GitHub users will not be notified. GitHub uploads still need to be processed after creating the changelog as the release notes must be published to upload assets to them. ([`e427658`](https://github.com/python-semantic-release/python-semantic-release/commit/e427658e33abf518191498c3142a0f18d3150e07)) ### Fix @@ -2843,7 +2843,7 @@ application/octet-stream is more generic, but it is better than using a non-offi * feat(history): capitalize changelog messages -Capitalize the first letter of messages in the changelog regardless of +Capitalize the first letter of messages in the changelog regardless of whether they are capitalized in the commit itself. ([`1a8e306`](https://github.com/python-semantic-release/python-semantic-release/commit/1a8e3060b8f6d6362c27903dcfc69d17db5f1d36)) ### Fix @@ -2881,7 +2881,7 @@ Fix the syntax of a broken bullet-point list in README.rst. ([`7aa572b`](https:/ * fix(github): send token in request header -Use an Authorization header instead of deprecated query parameter +Use an Authorization header instead of deprecated query parameter authorization. Fixes relekang/python-semantic-release#167 ([`be9972a`](https://github.com/python-semantic-release/python-semantic-release/commit/be9972a7b1fb183f738fb31bd370adb30281e4d5)) @@ -2952,13 +2952,13 @@ Fixes #156 ([`a4f8a10`](https://github.com/python-semantic-release/python-semant * fix: fallback to whole log if correct tag is not available (#157) -The method getting all commits to consider for the release will now test -whether the version in input is a valid reference. If it is not, it will -consider the whole log for the repository. - -evaluate_version_bump will still consider a message starting with the -version number as a breaking condition to stop analyzing. - +The method getting all commits to consider for the release will now test +whether the version in input is a valid reference. If it is not, it will +consider the whole log for the repository. + +evaluate_version_bump will still consider a message starting with the +version number as a breaking condition to stop analyzing. + Fixes #51 ([`252bffd`](https://github.com/python-semantic-release/python-semantic-release/commit/252bffd3be7b6dfcfdb384d24cb1cd83d990fc9a)) @@ -2968,14 +2968,14 @@ Fixes #51 ([`252bffd`](https://github.com/python-semantic-release/python-semanti * fix: Set version of click to >=2.0,<8.0. (#155) -* fix: Upgrade to click 7.0. - -Fixes #117 - -* fix: Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. - -* Upstream is at ~=7.0, so let's set the range to less than 8.0. - +* fix: Upgrade to click 7.0. + +Fixes #117 + +* fix: Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. + +* Upstream is at ~=7.0, so let's set the range to less than 8.0. + * The string template has no variables, so remove the call to .format() ([`f07c7f6`](https://github.com/python-semantic-release/python-semantic-release/commit/f07c7f653be1c018e443f071d9a196d9293e9521)) @@ -3166,9 +3166,9 @@ The commands is lacking from the documentation. ([`b6fa04d`](https://github.com/ ### Refactor -* refactor: added debug to hvcshvcs +* refactor: added debug to hvcshvcs + - module did not have any debug ([`0c6237b`](https://github.com/python-semantic-release/python-semantic-release/commit/0c6237bc01ec39608fb768925091c755d9bb25bd)) * refactor: fix import sorting ([`01e4c5d`](https://github.com/python-semantic-release/python-semantic-release/commit/01e4c5d743f2f237d2c85481118e467d4f5fde15)) @@ -3192,14 +3192,14 @@ module did not have any debug ([`0c6237b`](https://github.com/python-semantic-re * fix: Maintain version variable formatting on bump (#103) -Small change to the way the version is written to the config file it is read from. This allows the formatting to be the same as before semantic-release changed it. - -Prior behavior -`my_version_var="1.2.3"` => `my_version_var = '1.2.4'` - -New behavior -`my_version_var="1.2.3"` => `my_version_var="1.2.4"` - +Small change to the way the version is written to the config file it is read from. This allows the formatting to be the same as before semantic-release changed it. + +Prior behavior +`my_version_var="1.2.3"` => `my_version_var = '1.2.4'` + +New behavior +`my_version_var="1.2.3"` => `my_version_var="1.2.4"` + I am using python-semantic-release with a Julia project and this change will allow for consistent formatting in my Project.toml file where the version is maintained ([`bf63156`](https://github.com/python-semantic-release/python-semantic-release/commit/bf63156f60340614fae94c255fb2f097cf317b2b)) * fix: initialize git Repo from current folder @@ -3328,8 +3328,8 @@ Closes #88 re #32 ([`8df5e2b`](https://github.com/python-semantic-release/pytho * fix: Change requests from fixed version to version range (#93) -* Change requests version to be more flexible to aid in using this with dev requirements for a release. - +* Change requests version to be more flexible to aid in using this with dev requirements for a release. + * revert changes to vcs helpers ([`af3ad59`](https://github.com/python-semantic-release/python-semantic-release/commit/af3ad59f018876e11cc3acdda0b149f8dd5606bd)) ### Refactor @@ -3401,21 +3401,21 @@ Change the Gitpython version number to fix a bug described in #80. ([`23c9d4b`]( * feat: Add support to finding previous version from tags if not using commit messages (#68) -* feat: Be a bit more forgiving to find previous tags - -Now grabs the previous version from tag names if it can't find it in the commit - -* quantifiedcode and flake8 fixes - -* Update cli.py - +* feat: Be a bit more forgiving to find previous tags + +Now grabs the previous version from tag names if it can't find it in the commit + +* quantifiedcode and flake8 fixes + +* Update cli.py + * Switch to ImproperConfigurationError ([`6786487`](https://github.com/python-semantic-release/python-semantic-release/commit/6786487ebf4ab481139ef9f43cd74e345debb334)) * feat: Add --retry cli option (#78) -* Add --retry cli option -* Post changelog correctly -* Add comments +* Add --retry cli option +* Post changelog correctly +* Add comments * Add --retry to the docs ([`3e312c0`](https://github.com/python-semantic-release/python-semantic-release/commit/3e312c0ce79a78d25016a3b294b772983cfb5e0f)) ### Fix @@ -3451,9 +3451,9 @@ Fixes #74 ([`1dc306b`](https://github.com/python-semantic-release/python-semanti * fix: error when not in git repository (#75) -Fix an error when the program was run in a non-git repository. It would -not allow the help options to be run. - +Fix an error when the program was run in a non-git repository. It would +not allow the help options to be run. + issue #74 ([`251b190`](https://github.com/python-semantic-release/python-semantic-release/commit/251b190a2fd5df68892346926d447cbc1b32475a)) ### Unknown @@ -3507,12 +3507,12 @@ This reverts commit 93e5507da6d53ecf63405507390633ef480c52fb. ([`195ed8d`](https * feat: Add git hash to the changelog (#65) -* feat(*): add git hash to the changelog - -Add git hash to the changelog to ease finding the specific commit. The hash now is also easily viewable in Github's tag. see #63 for more information. - -* chore(test_history): fix test errors - +* feat(*): add git hash to the changelog + +Add git hash to the changelog to ease finding the specific commit. The hash now is also easily viewable in Github's tag. see #63 for more information. + +* chore(test_history): fix test errors + Fix the test errors that would happen after the modification of get_commit_log. ([`628170e`](https://github.com/python-semantic-release/python-semantic-release/commit/628170ebc440fc6abf094dd3e393f40576dedf9b)) ### Fix @@ -4226,7 +4226,7 @@ parsed. ([`974ccda`](https://github.com/python-semantic-release/python-semantic- * Merge pull request #15 from jezdez/python-2 -Add Python 2.7 support. Fix #10. +Add Python 2.7 support. Fix #10. :sparkles: ([`5daabb7`](https://github.com/python-semantic-release/python-semantic-release/commit/5daabb75eb9145566a2a7c2a9e64439df7cd85f1)) * Add Python 2.7 support. Fix #10. ([`c05e13f`](https://github.com/python-semantic-release/python-semantic-release/commit/c05e13f22163237e963c493ffeda7e140f0202c6)) diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index 0039800dc..8f422b120 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -371,13 +371,13 @@ def version( # noqa: C901 elif commit_changes: # TODO: in future this loop should be 1 line: - # repo.index.add(all_paths_to_add, force=False) + # repo.index.add(all_paths_to_add, force=False) # noqa: ERA001 # but since 'force' is deliberally ineffective (as in docstring) in gitpython 3.1.18 # we have to do manually add each filepath, and catch the exception if it is an ignored file for updated_path in all_paths_to_add: try: repo.git.add(updated_path) - except GitCommandError: + except GitCommandError: # noqa: PERF203 log.warning("Failed to add path (%s) to index", updated_path) rh = ReleaseHistory.from_git_history( @@ -445,13 +445,13 @@ def version( # noqa: C901 elif commit_changes: # Anything changed here should be staged. # TODO: in future this loop should be 1 line: - # repo.index.add(updated_paths, force=False) + # repo.index.add(updated_paths, force=False) # noqa: ERA001 # but since 'force' is deliberally ineffective (as in docstring) in gitpython 3.1.18 # we have to do manually add each filepath, and catch the exception if it is an ignored file for updated_path in updated_paths: try: repo.git.add(updated_path) - except GitCommandError: + except GitCommandError: # noqa: PERF203 log.warning("Failed to add path (%s) to index", updated_path) def custom_git_environment() -> ContextManager[None]: diff --git a/semantic_release/cli/config.py b/semantic_release/cli/config.py index 85fb4e30c..383443f44 100644 --- a/semantic_release/cli/config.py +++ b/semantic_release/cli/config.py @@ -113,7 +113,7 @@ class RemoteConfig(BaseModel): ignore_token_for_push: bool = False @model_validator(mode="after") - def set_default_token(self) -> "RemoteConfig": + def set_default_token(self) -> RemoteConfig: # Set the default token name for the given VCS when no user input is given if not self.token and self.type in _known_hvcs: default_token_name = _known_hvcs[self.type].DEFAULT_ENV_TOKEN_NAME diff --git a/semantic_release/hvcs/_base.py b/semantic_release/hvcs/_base.py index 86cd29c4d..05a459c5c 100644 --- a/semantic_release/hvcs/_base.py +++ b/semantic_release/hvcs/_base.py @@ -33,7 +33,7 @@ class HvcsBase: checking for NotImplemented around every method call. """ - DEFAULT_ENV_TOKEN_NAME = "HVCS_TOKEN" + DEFAULT_ENV_TOKEN_NAME = "HVCS_TOKEN" # noqa: S105 def __init__( self, diff --git a/semantic_release/hvcs/gitea.py b/semantic_release/hvcs/gitea.py index 4e4c28fdb..0138020ee 100644 --- a/semantic_release/hvcs/gitea.py +++ b/semantic_release/hvcs/gitea.py @@ -36,7 +36,7 @@ class Gitea(HvcsBase): DEFAULT_DOMAIN = "gitea.com" DEFAULT_API_PATH = "/api/v1" DEFAULT_API_DOMAIN = f"{DEFAULT_DOMAIN}{DEFAULT_API_PATH}" - DEFAULT_ENV_TOKEN_NAME = "GITEA_TOKEN" + DEFAULT_ENV_TOKEN_NAME = "GITEA_TOKEN" # noqa: S105 # pylint: disable=super-init-not-called def __init__( diff --git a/semantic_release/hvcs/github.py b/semantic_release/hvcs/github.py index 11a8bae8b..a147fb91c 100644 --- a/semantic_release/hvcs/github.py +++ b/semantic_release/hvcs/github.py @@ -37,7 +37,7 @@ class Github(HvcsBase): DEFAULT_DOMAIN = "github.com" DEFAULT_API_DOMAIN = "api.github.com" DEFAULT_UPLOAD_DOMAIN = "uploads.github.com" - DEFAULT_ENV_TOKEN_NAME = "GH_TOKEN" + DEFAULT_ENV_TOKEN_NAME = "GH_TOKEN" # noqa: S105 def __init__( self, diff --git a/semantic_release/hvcs/gitlab.py b/semantic_release/hvcs/gitlab.py index 1e4eb300c..5e23e5bae 100644 --- a/semantic_release/hvcs/gitlab.py +++ b/semantic_release/hvcs/gitlab.py @@ -38,7 +38,7 @@ class Gitlab(HvcsBase): API domain """ - DEFAULT_ENV_TOKEN_NAME = "GITLAB_TOKEN" + DEFAULT_ENV_TOKEN_NAME = "GITLAB_TOKEN" # noqa: S105 # purposefully not CI_JOB_TOKEN as it is not a personal access token, # It is missing the permission to push to the repository, but has all others (releases, packages, etc.) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 47ad02ce8..71ac6f5bb 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -8,8 +8,13 @@ import pytest import tomlkit -from semantic_release.commit_parser import * -from semantic_release.hvcs import * +from semantic_release.commit_parser import ( + AngularCommitParser, + EmojiCommitParser, + ScipyCommitParser, + TagCommitParser, +) +from semantic_release.hvcs import Gitea, Github, Gitlab from tests.const import ( EXAMPLE_CHANGELOG_MD_CONTENT, @@ -24,6 +29,9 @@ if TYPE_CHECKING: from typing import Any, Protocol + from semantic_release.commit_parser import CommitParser + from semantic_release.hvcs import HvcsBase + ExProjectDir = Path class UpdatePyprojectTomlFn(Protocol): @@ -40,18 +48,20 @@ def __call__(self) -> type[CommitParser]: @pytest.fixture -def change_to_tmp_dir(tmp_path: Path) -> Generator[None, None, None]: +def change_to_tmp_dir(tmp_path: Path) -> Generator[Path, None, None]: cwd = os.getcwd() os.chdir(str(tmp_path.resolve())) try: - yield + yield Path(os.getcwd()) finally: os.chdir(cwd) @pytest.fixture -def example_project(change_to_tmp_dir: None) -> ExProjectDir: - tmp_path = Path.cwd() +def example_project( + change_to_tmp_dir: Path, # noqa: U100 # must be given as an argument +) -> ExProjectDir: + tmp_path = change_to_tmp_dir src_dir = tmp_path / "src" src_dir.mkdir() example_dir = src_dir / EXAMPLE_PROJECT_NAME diff --git a/tests/unit/semantic_release/test_helpers.py b/tests/unit/semantic_release/test_helpers.py index 2a63ab2c0..1e37833fe 100644 --- a/tests/unit/semantic_release/test_helpers.py +++ b/tests/unit/semantic_release/test_helpers.py @@ -4,7 +4,7 @@ @pytest.mark.parametrize( - ("url", "expected"), + "url, expected", [ ( "http://git.mycompany.com/username/myproject.git", From 1d2bbdf23aab816cd9d6e26f537b5cec9e39dede Mon Sep 17 00:00:00 2001 From: semantic-release Date: Wed, 3 Jan 2024 20:34:53 +0000 Subject: [PATCH 039/167] 8.7.2 Automatically generated by python-semantic-release --- CHANGELOG.md | 1320 +++++++++++++++++----------------- pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 675 insertions(+), 649 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b18f6dbc..02a5da796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,32 @@ +## v8.7.2 (2024-01-03) + +### Build + +* build(deps-dev): bump ruff from 0.1.8 to 0.1.11 (#792) + +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.8 to 0.1.11. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.8...v0.1.11) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> +Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`6835fca`](https://github.com/python-semantic-release/python-semantic-release/commit/6835fcad54b452ac212ee132a8424453c1c1e150)) + +### Fix + +* fix(lint): correct linter errors ([`c9556b0`](https://github.com/python-semantic-release/python-semantic-release/commit/c9556b0ca6df6a61e9ce909d18bc5be8b6154bf8)) + + ## v8.7.1 (2024-01-03) ### Documentation @@ -61,55 +87,55 @@ Git remote url parsing now supports additional formats (ssh, https, file, git) ( * build(deps-dev): bump ruff from 0.1.7 to 0.1.8 (#775) -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.7 to 0.1.8. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.7...v0.1.8) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.7 to 0.1.8. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.7...v0.1.8) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`5efda8a`](https://github.com/python-semantic-release/python-semantic-release/commit/5efda8acfed938d3188cd55678ace20ecac7f798)) * build(deps-dev): bump ruff from 0.1.6 to 0.1.7 (#769) -* build(deps-dev): bump ruff from 0.1.6 to 0.1.7 - -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.6 to 0.1.7. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.6...v0.1.7) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> - -* ci: remove hardcoded ruff version in workflows - ---------- - -Signed-off-by: dependabot[bot] <support@github.com> -Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> +* build(deps-dev): bump ruff from 0.1.6 to 0.1.7 + +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.6 to 0.1.7. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.6...v0.1.7) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> + +* ci: remove hardcoded ruff version in workflows + +--------- + +Signed-off-by: dependabot[bot] <support@github.com> +Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> ([`c48c3b3`](https://github.com/python-semantic-release/python-semantic-release/commit/c48c3b370335931d63391d1a4f5802937deff178)) ### Fix * fix(cli): gracefully output configuration validation errors (#772) -* test(fixtures): update example project workflow & add config modifier - -* test(cli-main): add test for raw config validation error - +* test(fixtures): update example project workflow & add config modifier + +* test(cli-main): add test for raw config validation error + * fix(cli): gracefully output configuration validation errors ([`e8c9d51`](https://github.com/python-semantic-release/python-semantic-release/commit/e8c9d516c37466a5dce75a73766d5be0f9e74627)) ### Style @@ -125,26 +151,26 @@ Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> ([`c48c3b3`](htt * docs(configuration): adjust wording and improve clarity (#766) -* docs(configuration): fix typo in text - +* docs(configuration): fix typo in text + * docs(configuration): adjust wording and improve clarity ([`6b2fc8c`](https://github.com/python-semantic-release/python-semantic-release/commit/6b2fc8c156e122ee1b43fdb513b2dc3b8fd76724)) ### Fix * fix(config): gracefully fail when repo is in a detached HEAD state (#765) -* fix(config): cleanly handle repository in detached HEAD state - +* fix(config): cleanly handle repository in detached HEAD state + * test(cli-main): add detached head cli test ([`ac4f9aa`](https://github.com/python-semantic-release/python-semantic-release/commit/ac4f9aacb72c99f2479ae33369822faad011a824)) * fix(cmd-version): handle committing of git-ignored file gracefully (#764) -* fix(version): only commit non git-ignored files during version commit - -* test(version): set version file as ignored file - -Tweaks tests to use one committed change file and the version file -as an ignored change file. This allows us to verify that our commit +* fix(version): only commit non git-ignored files during version commit + +* test(version): set version file as ignored file + +Tweaks tests to use one committed change file and the version file +as an ignored change file. This allows us to verify that our commit mechanism does not crash if a file that is changed is ignored by user ([`ea89fa7`](https://github.com/python-semantic-release/python-semantic-release/commit/ea89fa72885e15da91687172355426a22c152513)) ### Style @@ -165,35 +191,35 @@ mechanism does not crash if a file that is changed is ignored by user ([`ea89fa7 * build(deps-dev): bump ruff from 0.1.2 to 0.1.6 (#757) -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.2 to 0.1.6. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.2...v0.1.6) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.2 to 0.1.6. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.2...v0.1.6) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`90db8f1`](https://github.com/python-semantic-release/python-semantic-release/commit/90db8f1bd8986eda1b913cd4bab5abd41192f01f)) * build(deps-dev): update python-gitlab requirement from <4,>=2 to >=2,<5 (#748) -Updates the requirements on [python-gitlab](https://github.com/python-gitlab/python-gitlab) to permit the latest version. -- [Release notes](https://github.com/python-gitlab/python-gitlab/releases) -- [Changelog](https://github.com/python-gitlab/python-gitlab/blob/main/CHANGELOG.md) -- [Commits](https://github.com/python-gitlab/python-gitlab/compare/v2.0.0...v4.1.1) - ---- -updated-dependencies: -- dependency-name: python-gitlab - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [python-gitlab](https://github.com/python-gitlab/python-gitlab) to permit the latest version. +- [Release notes](https://github.com/python-gitlab/python-gitlab/releases) +- [Changelog](https://github.com/python-gitlab/python-gitlab/blob/main/CHANGELOG.md) +- [Commits](https://github.com/python-gitlab/python-gitlab/compare/v2.0.0...v4.1.1) + +--- +updated-dependencies: +- dependency-name: python-gitlab + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`a176d62`](https://github.com/python-semantic-release/python-semantic-release/commit/a176d626f28ba68ae8a938b2f04f74da841a7eeb)) * build(deps-dev): bump ruff from 0.0.292 to 0.1.1 ([`9c5bbe0`](https://github.com/python-semantic-release/python-semantic-release/commit/9c5bbe0b0ef96e0fadae9e65918fc8939d0d3e60)) @@ -206,12 +232,12 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * feat(cmd-version): add `--tag/--no-tag` option to version command (#752) -* fix(version): separate push tags from commit push when not committing changes - -* feat(version): add `--no-tag` option to turn off tag creation - -* test(version): add test for `--tag` option & `--no-tag/commit` - +* fix(version): separate push tags from commit push when not committing changes + +* feat(version): add `--no-tag` option to turn off tag creation + +* test(version): add test for `--tag` option & `--no-tag/commit` + * docs(commands): update `version` subcommand options ([`de6b9ad`](https://github.com/python-semantic-release/python-semantic-release/commit/de6b9ad921e697b5ea2bb2ea8f180893cecca920)) ### Style @@ -286,19 +312,19 @@ GitHub.upload_asset now raises ValueError instead of requests.HTTPError ([`a13a6 * build(deps-dev): bump ruff from 0.0.286 to 0.0.290 (#713) -Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.286 to 0.0.290. -- [Release notes](https://github.com/astral-sh/ruff/releases) -- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md) -- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.286...v0.0.290) - ---- -updated-dependencies: -- dependency-name: ruff - dependency-type: direct:production - update-type: version-update:semver-patch -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.286 to 0.0.290. +- [Release notes](https://github.com/astral-sh/ruff/releases) +- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md) +- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.286...v0.0.290) + +--- +updated-dependencies: +- dependency-name: ruff + dependency-type: direct:production + update-type: version-update:semver-patch +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`6b288f2`](https://github.com/python-semantic-release/python-semantic-release/commit/6b288f2033366a8a2d6e730938df3606fa5ca5a7)) ### Fix @@ -312,12 +338,12 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 (#704) -* build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 - -* build(deps-dev): relax importlib-resources requirement - ---------- - +* build(deps-dev): bump importlib-resources from 5.7 to 5.12.0 + +* build(deps-dev): relax importlib-resources requirement + +--------- + Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`861fe01`](https://github.com/python-semantic-release/python-semantic-release/commit/861fe0119131f69761ae3e06ec46d62e526269d6)) ### Chore @@ -363,50 +389,50 @@ Co-authored-by: github-actions <action@github.com> ([`8a515ca`](https://gi * build(deps-dev): update pytest-xdist requirement (#677) -Updates the requirements on [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) to permit the latest version. -- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst) -- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.0.0...v3.3.1) - ---- -updated-dependencies: -- dependency-name: pytest-xdist - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) to permit the latest version. +- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst) +- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v2.0.0...v3.3.1) + +--- +updated-dependencies: +- dependency-name: pytest-xdist + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`9ec6321`](https://github.com/python-semantic-release/python-semantic-release/commit/9ec63215c8d60230ff90c06096d8f37956bd156a)) * build(deps-dev): update coverage[toml] requirement (#678) -Updates the requirements on [coverage[toml]](https://github.com/nedbat/coveragepy) to permit the latest version. -- [Release notes](https://github.com/nedbat/coveragepy/releases) -- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) -- [Commits](https://github.com/nedbat/coveragepy/compare/6.0...7.3.0) - ---- -updated-dependencies: -- dependency-name: coverage[toml] - dependency-type: direct:production -... - -Signed-off-by: dependabot[bot] <support@github.com> +Updates the requirements on [coverage[toml]](https://github.com/nedbat/coveragepy) to permit the latest version. +- [Release notes](https://github.com/nedbat/coveragepy/releases) +- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) +- [Commits](https://github.com/nedbat/coveragepy/compare/6.0...7.3.0) + +--- +updated-dependencies: +- dependency-name: coverage[toml] + dependency-type: direct:production +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`3561a97`](https://github.com/python-semantic-release/python-semantic-release/commit/3561a977fc56ec4bb12c9a894c0072cd8a3519fa)) * build(deps-dev): bump responses from 0.21.0 to 0.23.3 (#680) -Bumps [responses](https://github.com/getsentry/responses) from 0.21.0 to 0.23.3. -- [Release notes](https://github.com/getsentry/responses/releases) -- [Changelog](https://github.com/getsentry/responses/blob/master/CHANGES) -- [Commits](https://github.com/getsentry/responses/compare/0.21.0...0.23.3) - ---- -updated-dependencies: -- dependency-name: responses - dependency-type: direct:production - update-type: version-update:semver-minor -... - -Signed-off-by: dependabot[bot] <support@github.com> +Bumps [responses](https://github.com/getsentry/responses) from 0.21.0 to 0.23.3. +- [Release notes](https://github.com/getsentry/responses/releases) +- [Changelog](https://github.com/getsentry/responses/blob/master/CHANGES) +- [Commits](https://github.com/getsentry/responses/compare/0.21.0...0.23.3) + +--- +updated-dependencies: +- dependency-name: responses + dependency-type: direct:production + update-type: version-update:semver-minor +... + +Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ([`dd6946c`](https://github.com/python-semantic-release/python-semantic-release/commit/dd6946cd479f646f3af028b92ca83db8fc4c0c66)) ### Chore @@ -436,8 +462,8 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * fix: don't warn about vcs token if ignore_token_for_push is true. (#670) -* fix: don't warn about vcs token if ignore_token_for_push is true. - +* fix: don't warn about vcs token if ignore_token_for_push is true. + * docs: `password` should be `token`. ([`f1a54a6`](https://github.com/python-semantic-release/python-semantic-release/commit/f1a54a6c9a05b225b6474d50cd610eca19ec0c34)) ### Style @@ -503,184 +529,184 @@ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.githu * feat!: v8 (#619) -* feat!: 8.0.x (#538) - -Co-authored-by: Johan <johanhmr@gmail.com> -Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> - -* fix: correct Dockerfile CLI command and GHA fetch - -* fix: resolve branch checkout logic in GHA - -* fix: remove commit amending behaviour - -this was not working when there were no source code changes to be made, as it lead -to attempting to amend a HEAD commit that wasn't produced by PSR - -* 8.0.0-alpha.1 - -Automatically generated by python-semantic-release - -* fix: correct logic for generating release notes (#550) - -* fix: cleanup comments and unused logic - -* fix(action): mark container fs as safe for git to operate on - -* style: beautify 49080c510a68cccd2f6c7a8d540b483751901207 - -* fix(action): quotation for git config command - -* 8.0.0-alpha.2 - -Automatically generated by python-semantic-release - -* fix: resolve bug in changelog logic, enable upload to pypi - -* 8.0.0-alpha.3 - -Automatically generated by python-semantic-release - -* test: add tests for ReleaseHistory.release - -* fix: resolve loss of tag_format configuration - -* 8.0.0-alpha.4 - -Automatically generated by python-semantic-release - -* feat: various improvements - -* Added sorting to test parameterisation, so that pytest-xdist works again - dramatic speedup for testing -* Reworked the CI verification code so it's a bit prettier -* Added more testing for the version CLI command, and split some logic out of the command itself -* Removed a redundant double-regex match in VersionTranslator and Version, for some speedup - -* chore(test): proper env patching for tests in CI - -* style: beautify bcb27a4a8ce4789d083226f088c1810f45cd4c77 - -* refactor!: remove verify-ci command - -* 8.0.0-alpha.5 - -Automatically generated by python-semantic-release - -* fix(docs): fixup docs and remove reference to dist publication - -* feat!: remove publication of dists to artefact repository - -* feat: rename 'upload' configuration section to 'publish' - -* feat!: removed build status checking - -* feat: add GitHub Actions output - -* fix(action): remove default for 'force' - -* fix(ci): different workflow for v8 - -* fix(action): correct input parsing - -* fix: correct handling of build commands - -* feat: make it easier to access commit messages in ParsedCommits - -* fix: make additional attributes available for template authors - -* fix: add logging for token auth, use token for push - -* ci: add verbosity - -* fix: caching for repo owner and name - -* ci: contents permission for workflow - -* 8.0.0-alpha.6 - -Automatically generated by python-semantic-release - -* docs: update docs with additional required permissions - -* feat: add option to specify tag to publish to in publish command - -* feat: add Strict Mode - -* docs: convert to Furo theme - -* feat: add --skip-build option - -* 8.0.0-alpha.7 - -Automatically generated by python-semantic-release - -* test: separate command line tests by stdout and stderr - -* ci: pass tag output and conditionally execute publish steps - -* fix: correct assets type in configuration (#603) - -* change raw config assets type - -* fix: correct assets type-annotation for RuntimeContext - ---------- - -Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> - -* 8.0.0-alpha.8 - -Automatically generated by python-semantic-release - -* fix: pin Debian version in Dockerfile - -* feat: promote to rc - -* 8.0.0-rc.1 - -Automatically generated by python-semantic-release - -* ci: fix conditionals in workflow and update documentation - -* ci: correct conditionals - -* fix: only call Github Action output callback once defaults are set - -* 8.0.0-rc.2 - -Automatically generated by python-semantic-release - -* fix: create_or_update_release for Gitlab hvcs - -* ci: remove separate v8 workflow - -* chore: tweak issue templates - -* chore: bump docs dependencies - -* 8.0.0-rc.3 - -Automatically generated by python-semantic-release - -* fix(deps): add types-click, and downgrade sphinx/furo for 3.7 - -* 8.0.0-rc.4 - -Automatically generated by python-semantic-release - -* docs: fix typo (#623) - -* docs: correct typo in docs/changelog_templates.rst - -Co-authored-by: Micael Jarniac <micael@jarniac.com> - ---------- - -Co-authored-by: Johan <johanhmr@gmail.com> -Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> -Co-authored-by: semantic-release <semantic-release> -Co-authored-by: github-actions <action@github.com> -Co-authored-by: smeng9 <38666763+smeng9@users.noreply.github.com> +* feat!: 8.0.x (#538) + +Co-authored-by: Johan <johanhmr@gmail.com> +Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> + +* fix: correct Dockerfile CLI command and GHA fetch + +* fix: resolve branch checkout logic in GHA + +* fix: remove commit amending behaviour + +this was not working when there were no source code changes to be made, as it lead +to attempting to amend a HEAD commit that wasn't produced by PSR + +* 8.0.0-alpha.1 + +Automatically generated by python-semantic-release + +* fix: correct logic for generating release notes (#550) + +* fix: cleanup comments and unused logic + +* fix(action): mark container fs as safe for git to operate on + +* style: beautify 49080c510a68cccd2f6c7a8d540b483751901207 + +* fix(action): quotation for git config command + +* 8.0.0-alpha.2 + +Automatically generated by python-semantic-release + +* fix: resolve bug in changelog logic, enable upload to pypi + +* 8.0.0-alpha.3 + +Automatically generated by python-semantic-release + +* test: add tests for ReleaseHistory.release + +* fix: resolve loss of tag_format configuration + +* 8.0.0-alpha.4 + +Automatically generated by python-semantic-release + +* feat: various improvements + +* Added sorting to test parameterisation, so that pytest-xdist works again - dramatic speedup for testing +* Reworked the CI verification code so it's a bit prettier +* Added more testing for the version CLI command, and split some logic out of the command itself +* Removed a redundant double-regex match in VersionTranslator and Version, for some speedup + +* chore(test): proper env patching for tests in CI + +* style: beautify bcb27a4a8ce4789d083226f088c1810f45cd4c77 + +* refactor!: remove verify-ci command + +* 8.0.0-alpha.5 + +Automatically generated by python-semantic-release + +* fix(docs): fixup docs and remove reference to dist publication + +* feat!: remove publication of dists to artefact repository + +* feat: rename 'upload' configuration section to 'publish' + +* feat!: removed build status checking + +* feat: add GitHub Actions output + +* fix(action): remove default for 'force' + +* fix(ci): different workflow for v8 + +* fix(action): correct input parsing + +* fix: correct handling of build commands + +* feat: make it easier to access commit messages in ParsedCommits + +* fix: make additional attributes available for template authors + +* fix: add logging for token auth, use token for push + +* ci: add verbosity + +* fix: caching for repo owner and name + +* ci: contents permission for workflow + +* 8.0.0-alpha.6 + +Automatically generated by python-semantic-release + +* docs: update docs with additional required permissions + +* feat: add option to specify tag to publish to in publish command + +* feat: add Strict Mode + +* docs: convert to Furo theme + +* feat: add --skip-build option + +* 8.0.0-alpha.7 + +Automatically generated by python-semantic-release + +* test: separate command line tests by stdout and stderr + +* ci: pass tag output and conditionally execute publish steps + +* fix: correct assets type in configuration (#603) + +* change raw config assets type + +* fix: correct assets type-annotation for RuntimeContext + +--------- + +Co-authored-by: Bernard Cooke <bernard-cooke@hotmail.com> + +* 8.0.0-alpha.8 + +Automatically generated by python-semantic-release + +* fix: pin Debian version in Dockerfile + +* feat: promote to rc + +* 8.0.0-rc.1 + +Automatically generated by python-semantic-release + +* ci: fix conditionals in workflow and update documentation + +* ci: correct conditionals + +* fix: only call Github Action output callback once defaults are set + +* 8.0.0-rc.2 + +Automatically generated by python-semantic-release + +* fix: create_or_update_release for Gitlab hvcs + +* ci: remove separate v8 workflow + +* chore: tweak issue templates + +* chore: bump docs dependencies + +* 8.0.0-rc.3 + +Automatically generated by python-semantic-release + +* fix(deps): add types-click, and downgrade sphinx/furo for 3.7 + +* 8.0.0-rc.4 + +Automatically generated by python-semantic-release + +* docs: fix typo (#623) + +* docs: correct typo in docs/changelog_templates.rst + +Co-authored-by: Micael Jarniac <micael@jarniac.com> + +--------- + +Co-authored-by: Johan <johanhmr@gmail.com> +Co-authored-by: U-NEO\johan <johan.hammar@ombea.com> +Co-authored-by: semantic-release <semantic-release> +Co-authored-by: github-actions <action@github.com> +Co-authored-by: smeng9 <38666763+smeng9@users.noreply.github.com> Co-authored-by: Micael Jarniac <micael@jarniac.com> ([`ec30564`](https://github.com/python-semantic-release/python-semantic-release/commit/ec30564b4ec732c001d76d3c09ba033066d2b6fe)) @@ -704,11 +730,11 @@ Co-authored-by: Micael Jarniac <micael@jarniac.com> ([`ec30564`](https://g * fix: docker build fails installing git (#605) -git was installed from bullseye-backports, but base image is referencing latest python:3.10 -since bookworm was recently released, this now points at bookworm and installing the backport of git is actually trying to downgrade, resulting in this error: - -> E: Packages were downgraded and -y was used without --allow-downgrades. - +git was installed from bullseye-backports, but base image is referencing latest python:3.10 +since bookworm was recently released, this now points at bookworm and installing the backport of git is actually trying to downgrade, resulting in this error: + +> E: Packages were downgraded and -y was used without --allow-downgrades. + > ERROR: failed to solve: process "/bin/sh -c echo \"deb http://deb.debian.org/debian bullseye-backports main\" >> /etc/apt/sources.list; apt-get update; apt-get install -y git/bullseye-backports" did not complete successfully: exit code: 100 ([`9e3eb97`](https://github.com/python-semantic-release/python-semantic-release/commit/9e3eb979783bc39ca564c2967c6c77eecba682e6)) @@ -734,7 +760,7 @@ In #594, I missed that there are 2 places where the version header is formatted * fix: generate markdown linter compliant changelog headers & lists (#594) -Add an extra new line after each header and between sections to fix 2 markdownlint errors +Add an extra new line after each header and between sections to fix 2 markdownlint errors for changelogs generated by this package ([`9d9d403`](https://github.com/python-semantic-release/python-semantic-release/commit/9d9d40305c499c907335abe313e3ed122db0b154)) ### Style @@ -748,9 +774,9 @@ for changelogs generated by this package ([`9d9d403`](https://github.com/python- * feat: add option to only parse commits for current working directory (#509) -When running the application from a sub-directory in the VCS, the option -use_only_cwd_commits will filter out commits that does not changes the -current working directory, similar to running commands like +When running the application from a sub-directory in the VCS, the option +use_only_cwd_commits will filter out commits that does not changes the +current working directory, similar to running commands like `git log -- .` in a sub-directory. ([`cdf8116`](https://github.com/python-semantic-release/python-semantic-release/commit/cdf8116c1e415363b10a01f541873e04ad874220)) @@ -760,22 +786,22 @@ current working directory, similar to running commands like * docs: update broken badge and add links (#591) -The "Test Status" badge was updated to address a recent breaking change in the -GitHub actions API. All the badges updated to add links to the appropriate +The "Test Status" badge was updated to address a recent breaking change in the +GitHub actions API. All the badges updated to add links to the appropriate resources for end-user convenience. ([`0c23447`](https://github.com/python-semantic-release/python-semantic-release/commit/0c234475d27ad887b19170c82deb80293b3a95f1)) ### Fix * fix: update docs and default config for gitmoji changes (#590) -* fix: update docs and default config for gitmoji changes - -PR #582 updated to the latest Gitmojis release however the documentation and -default config options still referenced old and unsupported Gitmojis. - -* fix: update sphinx dep - -I could only build the documentation locally by updating Sphinx to the latest +* fix: update docs and default config for gitmoji changes + +PR #582 updated to the latest Gitmojis release however the documentation and +default config options still referenced old and unsupported Gitmojis. + +* fix: update sphinx dep + +I could only build the documentation locally by updating Sphinx to the latest 1.x version. ([`192da6e`](https://github.com/python-semantic-release/python-semantic-release/commit/192da6e1352298b48630423d50191070a1c5ab24)) @@ -802,16 +828,16 @@ In order to avoid 'Repository not found: relekang/python-semantic-release.&# * docs: spelling and grammar in `travis.rst` (#556) -- spelling -- subject-verb agreement -- remove verbiage - +- spelling +- subject-verb agreement +- remove verbiage + Signed-off-by: Vladislav Doster <mvdoster@gmail.com> ([`3a76e9d`](https://github.com/python-semantic-release/python-semantic-release/commit/3a76e9d7505c421009eb3e953c32cccac2e70e07)) * docs: grammar in `docs/troubleshooting.rst` (#557) -- change contraction to a possessive pronoun - +- change contraction to a possessive pronoun + Signed-off-by: Vladislav Doster <mvdoster@gmail.com> ([`bbe754a`](https://github.com/python-semantic-release/python-semantic-release/commit/bbe754a3db9ce7132749e7902fe118b52f48ee42)) ### Fix @@ -859,11 +885,11 @@ It is spamming to much. We can bring it back if we get the time to fix the spamm * ci: remove python3.6 from GHA, add python3.10 and python3.11 (#541) -* ci: remove python3.6 from GHA, add python3.10 and python3.11 - -GHA workflows are failing without this, due to -https://github.com/actions/setup-python/issues/544\#issuecomment-1332535877 - +* ci: remove python3.6 from GHA, add python3.10 and python3.11 + +GHA workflows are failing without this, due to +https://github.com/actions/setup-python/issues/544\#issuecomment-1332535877 + * fix: upgrade pytest ([`8e4aa0e`](https://github.com/python-semantic-release/python-semantic-release/commit/8e4aa0e30438291ade98604a18aeb372f0d0b52f)) ### Feature @@ -882,17 +908,17 @@ Co-authored-by: Kevin Watson <Kevmo92@users.noreply.github.com> ([`4664afe * fix: changelog release commit search logic (#530) -* Fixes changelog release commit search logic - -Running `semantic-release changelog` currently fails to identify "the last commit in [a] release" because the compared commit messages have superfluous whitespace. -Likely related to the issue causing: https://github.com/relekang/python-semantic-release/issues/490 - +* Fixes changelog release commit search logic + +Running `semantic-release changelog` currently fails to identify "the last commit in [a] release" because the compared commit messages have superfluous whitespace. +Likely related to the issue causing: https://github.com/relekang/python-semantic-release/issues/490 + * Removes a couple of extra `strip()`s. ([`efb3410`](https://github.com/python-semantic-release/python-semantic-release/commit/efb341036196c39b4694ca4bfa56c6b3e0827c6c)) * fix: bump Dockerfile to use Python 3.10 image (#536) -Fixes #533 - +Fixes #533 + Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`8f2185d`](https://github.com/python-semantic-release/python-semantic-release/commit/8f2185d570b3966b667ac591ae523812e9d2e00f)) * fix: fix mypy errors for publish ([`b40dd48`](https://github.com/python-semantic-release/python-semantic-release/commit/b40dd484387c1b3f78df53ee2d35e281e8e799c8)) @@ -918,10 +944,10 @@ Co-authored-by: Bernard Cooke <bernard.cooke@iotics.com> ([`8f2185d`](http * ci: Update deprecated actions (#511) -* ci: update depreated actions - -* ci: replace deprecated set-output in workflow - +* ci: update depreated actions + +* ci: replace deprecated set-output in workflow + According to https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ ([`bb09233`](https://github.com/python-semantic-release/python-semantic-release/commit/bb09233b84d153a15784fdf68d7274c9d682c336)) ### Documentation @@ -1092,9 +1118,9 @@ Fixes #473 ([`0ece6f2`](https://github.com/python-semantic-release/python-semant * fix: allow changing prerelease tag using CLI flags (#466) -Delay construction of version and release patterns until runtime. -This will allow to use non-default prerelease tag. - +Delay construction of version and release patterns until runtime. +This will allow to use non-default prerelease tag. + Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`395bf4f`](https://github.com/python-semantic-release/python-semantic-release/commit/395bf4f2de73663c070f37cced85162d41934213)) @@ -1104,8 +1130,8 @@ Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`395bf4f`](http * fix(publish): get version bump for current release (#467) -Replicate the behavior of "version" command in version calculation. - +Replicate the behavior of "version" command in version calculation. + Co-authored-by: Dzmitry Ryzhykau <d.ryzhykau@onesoil.ai> ([`dd26888`](https://github.com/python-semantic-release/python-semantic-release/commit/dd26888a923b2f480303c19f1916647de48b02bf)) * fix: add packaging module requirement (#469) ([`b99c9fa`](https://github.com/python-semantic-release/python-semantic-release/commit/b99c9fa88dc25e5ceacb131cd93d9079c4fb2c86)) @@ -1179,33 +1205,33 @@ Co-authored-by: Sebastian Seith <sebastian@vermill.io> ([`da0606f`](https: * ci: adjust actions test phase to use fetch-depth: 0 to fix ci tests (#446) -Co-authored-by: Sebastian Seith <sebastian@vermill.io> +Co-authored-by: Sebastian Seith <sebastian@vermill.io> Co-authored-by: github-actions <action@github.com> ([`3329eef`](https://github.com/python-semantic-release/python-semantic-release/commit/3329eeffb077f628e4a965bc7fd922d09d6b63da)) ### Feature * feat: allow using ssh-key to push version while using token to publish to hvcs (#419) -* feat(config): add ignore_token_for_push param - -Add ignore_token_for_push parameter that allows using the underlying -git authentication mechanism for pushing a new version commit and tags -while also using an specified token to upload dists - -* test(config): add test for ignore_token_for_push - -Test push_new_version with token while ignore_token_for_push is True -and False - -* docs: add documentation for ignore_token_for_push - -* fix(test): override GITHUB_ACTOR env - -push_new_version is using GITHUB_ACTOR env var but we did not -contemplate in our new tests that actually Github actions running the -tests will populate that var and change the test outcome - -Now we control the value of that env var and test for it being present +* feat(config): add ignore_token_for_push param + +Add ignore_token_for_push parameter that allows using the underlying +git authentication mechanism for pushing a new version commit and tags +while also using an specified token to upload dists + +* test(config): add test for ignore_token_for_push + +Test push_new_version with token while ignore_token_for_push is True +and False + +* docs: add documentation for ignore_token_for_push + +* fix(test): override GITHUB_ACTOR env + +push_new_version is using GITHUB_ACTOR env var but we did not +contemplate in our new tests that actually Github actions running the +tests will populate that var and change the test outcome + +Now we control the value of that env var and test for it being present or not ([`7b2dffa`](https://github.com/python-semantic-release/python-semantic-release/commit/7b2dffadf43c77d5e0eea307aefcee5c7744df5c)) ### Fix @@ -1243,16 +1269,16 @@ Fixes #354 ([`cf74339`](https://github.com/python-semantic-release/python-semant * chore(dependencies): unpin tomlkit dependency (#429) -- tests for a tomlkit regression don't fail anymore with newer tomlkit -- keep comment in setup.py about tomlkit being pinned at some point in time - +- tests for a tomlkit regression don't fail anymore with newer tomlkit +- keep comment in setup.py about tomlkit being pinned at some point in time + refs #336 ([`8515879`](https://github.com/python-semantic-release/python-semantic-release/commit/85158798ca438c1dafc84036d13c2988c934f02f)) ### Fix * fix(prerelase): pass prerelease option to get_current_version (#432) -The `get_current_version` function accepts a `prerelease` argument which +The `get_current_version` function accepts a `prerelease` argument which was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python-semantic-release/commit/aabab0b7ce647d25e0c78ae6566f1132ece9fcb9)) ### Style @@ -1266,7 +1292,7 @@ was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python * chore(dependencies): extend allowed version range for python-gitlab (#417) -* chore(dependencies): extend allowed version range for python-gitlab +* chore(dependencies): extend allowed version range for python-gitlab * fix(type): ignore mypy errors for dynamic RESTObject ([`8ee4d4b`](https://github.com/python-semantic-release/python-semantic-release/commit/8ee4d4b8dabfa5c6cd2aa6180d4a8da8f3c9554c)) ### Feature @@ -1280,18 +1306,18 @@ was never passed. ([`aabab0b`](https://github.com/python-semantic-release/python * feat: add prerelease functionality (#413) -* chore: add initial todos -* feat: add prerelease tag option -* feat: add prerelease cli flag -* feat: omit_pattern for previouse and current version getters -* feat: print_version with prerelease bump -* feat: make print_version prerelease ready -* feat: move prerelease determination to get_new_version -* test: improve get_last_version test -* docs: added basic infos about prereleases -* feat: add prerelease flag to version and publish -* feat: remove leftover todos - +* chore: add initial todos +* feat: add prerelease tag option +* feat: add prerelease cli flag +* feat: omit_pattern for previouse and current version getters +* feat: print_version with prerelease bump +* feat: make print_version prerelease ready +* feat: move prerelease determination to get_new_version +* test: improve get_last_version test +* docs: added basic infos about prereleases +* feat: add prerelease flag to version and publish +* feat: remove leftover todos + Co-authored-by: Mario Jäckle <m.jaeckle@careerpartner.eu> ([`7064265`](https://github.com/python-semantic-release/python-semantic-release/commit/7064265627a2aba09caa2873d823b594e0e23e77)) ### Style @@ -1312,7 +1338,7 @@ Co-authored-by: Mario Jäckle <m.jaeckle@careerpartner.eu> ([`7064265`](ht * fix(gitea): build status and asset upload (#420) -* fix(gitea): handle list build status response +* fix(gitea): handle list build status response * fix(gitea): use form-data for upload_asset ([`57db81f`](https://github.com/python-semantic-release/python-semantic-release/commit/57db81f4c6b96da8259e3bad9137eaccbcd10f6e)) ### Style @@ -1444,10 +1470,10 @@ executable. No-op mode now respected by artifact upload. ([`cfb20af`](https://gi * feat: allow custom environment variable names (#392) -* GH_TOKEN can now be customized by setting github_token_var -* GL_TOKEN can now be customized by setting gitlab_token_var -* PYPI_PASSWORD can now be customized by setting pypi_pass_var -* PYPI_TOKEN can now be customized by setting pypi_token_var +* GH_TOKEN can now be customized by setting github_token_var +* GL_TOKEN can now be customized by setting gitlab_token_var +* PYPI_PASSWORD can now be customized by setting pypi_pass_var +* PYPI_TOKEN can now be customized by setting pypi_token_var * PYPI_USERNAME can now be customized by setting pypi_user_var ([`372cda3`](https://github.com/python-semantic-release/python-semantic-release/commit/372cda3497f16ead2209e6e1377d38f497144883)) * feat: use gitlab-ci or github actions env vars @@ -1515,8 +1541,8 @@ https://github.com/relekang/python-semantic-release/issues/391#issuecomment-9506 * feat: custom git tag format support (#373) -* feat: custom git tag format support -* test: add git tag format check +* feat: custom git tag format support +* test: add git tag format check * docs: add tag_format config option ([`1d76632`](https://github.com/python-semantic-release/python-semantic-release/commit/1d76632043bf0b6076d214a63c92013624f4b95e)) @@ -1562,9 +1588,9 @@ Co-authored-by: Laercio Barbosa <laercio.barbosa@scania.com> ([`a275a7a`]( * chore: bump responses to latest version (#343) -The current version has a Deprecation warning: - -inspect.getargspec() is deprecated since Python 3.0, +The current version has a Deprecation warning: + +inspect.getargspec() is deprecated since Python 3.0, use inspect.signature() or inspect.getfullargspec() ([`e953157`](https://github.com/python-semantic-release/python-semantic-release/commit/e953157125f4528759453218f75b6e51cafd2cc3)) ### Ci @@ -1747,23 +1773,23 @@ Fixes #329 ([`abfacc4`](https://github.com/python-semantic-release/python-semant * docs: correct casing on proper nouns (#320) -* docs: correcting Semantic Versioning casing - -Semantic Versioning is the name of the specification. -Therefore it is a proper noun. -This patch corrects the incorrect casing for Semantic Versioning. - -* docs: correcting Python casing - +* docs: correcting Semantic Versioning casing + +Semantic Versioning is the name of the specification. +Therefore it is a proper noun. +This patch corrects the incorrect casing for Semantic Versioning. + +* docs: correcting Python casing + This patch corrects the incorrect casing for Python. ([`d51b999`](https://github.com/python-semantic-release/python-semantic-release/commit/d51b999a245a4e56ff7a09d0495c75336f2f150d)) ### Feature * feat(checks): add support for Jenkins CI (#322) -Includes a ci check handler to verify jenkins. -Unlike other ci systems jenkins doesn't generally prefix things with -`JENKINS` or simply inject `JENKINS=true` Really the only thing that is +Includes a ci check handler to verify jenkins. +Unlike other ci systems jenkins doesn't generally prefix things with +`JENKINS` or simply inject `JENKINS=true` Really the only thing that is immediately identifiable is `JENKINS_URL` ([`3e99855`](https://github.com/python-semantic-release/python-semantic-release/commit/3e99855c6bc72b3e9a572c58cc14e82ddeebfff8)) ### Style @@ -1781,10 +1807,10 @@ immediately identifiable is `JENKINS_URL` ([`3e99855`](https://github.com/python * fix: fix crash when TOML has no PSR section (#319) -* test: reproduce issue with TOML without PSR section - -* fix: crash when TOML has no PSR section - +* test: reproduce issue with TOML without PSR section + +* fix: crash when TOML has no PSR section + * chore: remove unused imports ([`5f8ab99`](https://github.com/python-semantic-release/python-semantic-release/commit/5f8ab99bf7254508f4b38fcddef2bdde8dd15a4c)) @@ -1807,26 +1833,26 @@ Fixes #306 ([`1a85af4`](https://github.com/python-semantic-release/python-semant * feat: support toml files for version declaration (#307) -This introduce a new `version_toml` configuration property that behaves -like `version_pattern` and `version_variable`. - -For poetry support, user should now set `version_toml = pyproject.toml:tool.poetry.version`. - -This introduce an ABC class, `VersionDeclaration`, that -can be implemented to add other version declarations with ease. - -`toml` dependency has been replaced by `tomlkit`, as this is used -the library used by poetry to generate the `pyproject.toml` file, and -is able to keep the ordering of data defined in the file. - -Existing `VersionPattern` class has been renamed to -`PatternVersionDeclaration` and now implements `VersionDeclaration`. - -`load_version_patterns()` function has been renamed to -`load_version_declarations()` and now return a list of -`VersionDeclaration` implementations. - -Close #245 +This introduce a new `version_toml` configuration property that behaves +like `version_pattern` and `version_variable`. + +For poetry support, user should now set `version_toml = pyproject.toml:tool.poetry.version`. + +This introduce an ABC class, `VersionDeclaration`, that +can be implemented to add other version declarations with ease. + +`toml` dependency has been replaced by `tomlkit`, as this is used +the library used by poetry to generate the `pyproject.toml` file, and +is able to keep the ordering of data defined in the file. + +Existing `VersionPattern` class has been renamed to +`PatternVersionDeclaration` and now implements `VersionDeclaration`. + +`load_version_patterns()` function has been renamed to +`load_version_declarations()` and now return a list of +`VersionDeclaration` implementations. + +Close #245 Close #275 ([`9b62a7e`](https://github.com/python-semantic-release/python-semantic-release/commit/9b62a7e377378667e716384684a47cdf392093fa)) ### Style @@ -1846,8 +1872,8 @@ Fixes #311 ([`e2d8e47`](https://github.com/python-semantic-release/python-semant * feat(github): retry GitHub API requests on failure (#314) -* refactor(github): use requests.Session to call raise_for_status - +* refactor(github): use requests.Session to call raise_for_status + * fix(github): add retries to github API requests ([`ac241ed`](https://github.com/python-semantic-release/python-semantic-release/commit/ac241edf4de39f4fc0ff561a749fa85caaf9e2ae)) ### Style @@ -1941,10 +1967,10 @@ Fixes: #277 ([`ab3061a`](https://github.com/python-semantic-release/python-seman * fix(history): coerce version to string (#298) -The changes in #297 mistakenly omitted coercing the return value to a -string. This resulted in errors like: -"can only concatenate str (not "VersionInfo") to str" - +The changes in #297 mistakenly omitted coercing the return value to a +string. This resulted in errors like: +"can only concatenate str (not "VersionInfo") to str" + Add test case asserting it's type str ([`d4cdc3d`](https://github.com/python-semantic-release/python-semantic-release/commit/d4cdc3d3cd2d93f2a78f485e3ea107ac816c7d00)) * fix(history): require semver >= 2.10 @@ -2020,8 +2046,8 @@ the end of a PR that is merged via rebase merge or merge commit. ([`93e48c9`](ht * feat(changelog): add PR links in markdown (#282) -GitHub release notes automagically link to the PR, but changelog -markdown doesn't. Replace a PR number at the end of a message +GitHub release notes automagically link to the PR, but changelog +markdown doesn't. Replace a PR number at the end of a message with a markdown link. ([`0448f6c`](https://github.com/python-semantic-release/python-semantic-release/commit/0448f6c350bbbf239a81fe13dc5f45761efa7673)) ### Style @@ -2045,7 +2071,7 @@ To control if bump major or not when current major version is zero. ([`d324154`] * refactor(history): move changelog_scope default (#284) -* Move the default for changelog_scope from inline to defaults.cfg. +* Move the default for changelog_scope from inline to defaults.cfg. * Add missing header in docs. ([`b7e1376`](https://github.com/python-semantic-release/python-semantic-release/commit/b7e1376ee1688e5e6dcc069ce623f49e3a389052)) ### Style @@ -2061,13 +2087,13 @@ A few settings were not in alphabetical order. ([`60a3535`](https://github.com/p * feat(logs): include scope in changelogs (#281) -When the scope is set, include it in changelogs, e.g. -"feat(x): some description" becomes "**x**: some description". -This is similar to how the Node semantic release (and -conventional-changelog-generator) generates changelogs. -If scope is not given, it's omitted. - -Add a new config parameter changelog_scope to disable this behavior when +When the scope is set, include it in changelogs, e.g. +"feat(x): some description" becomes "**x**: some description". +This is similar to how the Node semantic release (and +conventional-changelog-generator) generates changelogs. +If scope is not given, it's omitted. + +Add a new config parameter changelog_scope to disable this behavior when set to 'False' ([`21c96b6`](https://github.com/python-semantic-release/python-semantic-release/commit/21c96b688cc44cc6f45af962ffe6d1f759783f37)) ### Style @@ -2113,11 +2139,11 @@ Fixes #260 ([`7cacca1`](https://github.com/python-semantic-release/python-semant * ci: check commit logs with commitlint (#263) -The contributing guide says that the project should itself follow the -Angular commit convention, but there is nothing to enforce it AFAIK. - -I had a similar problem on a project where I'm using -`python-semantic-release` and I've added a Github action to +The contributing guide says that the project should itself follow the +Angular commit convention, but there is nothing to enforce it AFAIK. + +I had a similar problem on a project where I'm using +`python-semantic-release` and I've added a Github action to test it on CI, you might find it useful too. ([`016fde6`](https://github.com/python-semantic-release/python-semantic-release/commit/016fde683924d380d25579bd0cff0c7f8b7b2240)) ### Documentation @@ -2142,7 +2168,7 @@ the method above. ([`5a5e2cf`](https://github.com/python-semantic-release/python * fix: add required to inputs in action metadata (#264) -According to the documentation, `inputs.<input_id>.required` is a +According to the documentation, `inputs.<input_id>.required` is a required field. ([`e76b255`](https://github.com/python-semantic-release/python-semantic-release/commit/e76b255cf7d3d156e3314fc28c54d63fa126e973)) @@ -2199,8 +2225,8 @@ Fixes #250 ([`87e2bb8`](https://github.com/python-semantic-release/python-semant * docs: give example of multiple build commands (#248) -I had a little trouble figuring out how to use a non-setup.py build -command, so I thought it would be helpful to update the docs with an +I had a little trouble figuring out how to use a non-setup.py build +command, so I thought it would be helpful to update the docs with an example of how to do this. ([`65f1ffc`](https://github.com/python-semantic-release/python-semantic-release/commit/65f1ffcc6cac3bf382f4b821ff2be59d04f9f867)) ### Fix @@ -2218,13 +2244,13 @@ example of how to do this. ([`65f1ffc`](https://github.com/python-semantic-relea * feat: bump versions in multiple files (#246) -- Add the `version_pattern` setting, which allows version numbers to be - identified using arbitrary regular expressions. -- Refactor the config system to allow non-string data types to be - specified in `pyproject.toml`. -- Multiple files can now be specified by setting `version_variable` or - `version_pattern` to a list in `pyproject.toml`. - +- Add the `version_pattern` setting, which allows version numbers to be + identified using arbitrary regular expressions. +- Refactor the config system to allow non-string data types to be + specified in `pyproject.toml`. +- Multiple files can now be specified by setting `version_variable` or + `version_pattern` to a list in `pyproject.toml`. + Fixes #175 ([`0ba2c47`](https://github.com/python-semantic-release/python-semantic-release/commit/0ba2c473c6e44cc326b3299b6ea3ddde833bdb37)) ### Style @@ -2245,9 +2271,9 @@ Fixes #175 ([`0ba2c47`](https://github.com/python-semantic-release/python-semant * feat(changelog): add changelog_table component (#242) -Add an alternative changelog component which displays each section as a -row in a table. - +Add an alternative changelog component which displays each section as a +row in a table. + Fixes #237 ([`fe6a7e7`](https://github.com/python-semantic-release/python-semantic-release/commit/fe6a7e7fa014ffb827a1430dbcc10d1fc84c886b)) ### Style @@ -2265,25 +2291,25 @@ Fixes #237 ([`fe6a7e7`](https://github.com/python-semantic-release/python-semant * feat(changelog): add changelog components (#240) -* feat(changelog): add changelog components - -Add the ability to configure sections of the changelog using a -`changelog_components` option. Component outputs are separated by a blank -line and appear in the same order as they were configured. - -It is possible to create your own custom components. Each component is a -function which returns either some text to be added, or None in which case it -will be skipped. - -BREAKING CHANGE: The `compare_url` option has been removed in favor of using -`changelog_components`. This functionality is now available as the -`semantic_release.changelog.compare_url` component. - -* docs: add documentation for changelog_components - -* feat: pass changelog_sections to components - -Changelog components may now receive the value of `changelog_sections`, +* feat(changelog): add changelog components + +Add the ability to configure sections of the changelog using a +`changelog_components` option. Component outputs are separated by a blank +line and appear in the same order as they were configured. + +It is possible to create your own custom components. Each component is a +function which returns either some text to be added, or None in which case it +will be skipped. + +BREAKING CHANGE: The `compare_url` option has been removed in favor of using +`changelog_components`. This functionality is now available as the +`semantic_release.changelog.compare_url` component. + +* docs: add documentation for changelog_components + +* feat: pass changelog_sections to components + +Changelog components may now receive the value of `changelog_sections`, split and ready to use. ([`3e17a98`](https://github.com/python-semantic-release/python-semantic-release/commit/3e17a98d7fa8468868a87e62651ac2c010067711)) ### Style @@ -2310,30 +2336,30 @@ Fixes #239 ([`34acbbc`](https://github.com/python-semantic-release/python-semant * feat(history): create emoji parser (#238) -Add a commit parser which uses emojis from https://gitmoji.carloscuesta.me/ -to determine the type of change. - -* fix: add emojis to default changelog_sections - -* fix: include all parsed types in changelog - -This allows emojis to appear in the changelog, as well as configuring -other types to appear with the Angular parser (I remember someone asking -for that feature a while ago). All filtering is now done in the -markdown_changelog function. - -* refactor(history): get breaking changes in parser - -Move the task of detecting breaking change descriptions into the commit -parser function, instead of during changelog generation. - -This has allowed the emoji parser to also return the regular descriptions as -breaking change descriptions for commits with :boom:. - -BREAKING CHANGE: Custom commit parser functions are now required to pass -a fifth argument to `ParsedCommit`, which is a list of breaking change -descriptions. - +Add a commit parser which uses emojis from https://gitmoji.carloscuesta.me/ +to determine the type of change. + +* fix: add emojis to default changelog_sections + +* fix: include all parsed types in changelog + +This allows emojis to appear in the changelog, as well as configuring +other types to appear with the Angular parser (I remember someone asking +for that feature a while ago). All filtering is now done in the +markdown_changelog function. + +* refactor(history): get breaking changes in parser + +Move the task of detecting breaking change descriptions into the commit +parser function, instead of during changelog generation. + +This has allowed the emoji parser to also return the regular descriptions as +breaking change descriptions for commits with :boom:. + +BREAKING CHANGE: Custom commit parser functions are now required to pass +a fifth argument to `ParsedCommit`, which is a list of breaking change +descriptions. + * docs: add documentation for emoji parser ([`2e1c50a`](https://github.com/python-semantic-release/python-semantic-release/commit/2e1c50a865628b372f48945a039a3edb38a7cdf0)) ### Style @@ -2407,12 +2433,12 @@ Any issues which are labelled as a question will be closed after two weeks of in * ci: pass SHA from beautify to release -Checkout the current SHA from the end of the beautify job for releasing, -instead of master. This will either be the same as the commit we are -running for, or the SHA of a style commit. This prevents releasing of +Checkout the current SHA from the end of the beautify job for releasing, +instead of master. This will either be the same as the commit we are +running for, or the SHA of a style commit. This prevents releasing of untested code. -See +See https://github.community/t5/GitHub-Actions/Checkout-commit-pushed-by-previous-job/m-p/55847#M9670 ([`76e34b6`](https://github.com/python-semantic-release/python-semantic-release/commit/76e34b6b52b8019e87eaddf295d0781b6aa51541)) ### Documentation @@ -2490,15 +2516,15 @@ This was missed in 213530fb0c914e274b81d1dacf38ea7322b5b91f ([`3084249`](https:/ * refactor(debug): use logging and click_log instead of ndebug -BREAKING CHANGE: `DEBUG="*"` no longer has an effect, instead use +BREAKING CHANGE: `DEBUG="*"` no longer has an effect, instead use `--verbosity DEBUG`. ([`15b1f65`](https://github.com/python-semantic-release/python-semantic-release/commit/15b1f650f29761e1ab2a91b767cbff79b2057a4c)) ### Build * build(pip): store requirements in setup.py -Remove the requirements directory and instead store all required -libraries directly inside setup.py. Development, testing and docs +Remove the requirements directory and instead store all required +libraries directly inside setup.py. Development, testing and docs dependencies are included as extras. ([`401468f`](https://github.com/python-semantic-release/python-semantic-release/commit/401468f312cf4f3b52006c68c58c4645b5e19802)) ### Chore @@ -2511,12 +2537,12 @@ Allow mypy and coverage to run on any Python version. ([`28feba6`](https://githu * ci: always checkout most recent commit to release -This should pull a beautify commit if one has been created, allowing the +This should pull a beautify commit if one has been created, allowing the new version to be pushed. ([`6c98aab`](https://github.com/python-semantic-release/python-semantic-release/commit/6c98aab932724e3aab08e68b75439bc8c31bd877)) * ci: cache testing dependencies -This should help improve the speed of the testing workflow by caching +This should help improve the speed of the testing workflow by caching downloaded dependencies. ([`4f53e35`](https://github.com/python-semantic-release/python-semantic-release/commit/4f53e351960a6b658f50265384c9e8f678718f68)) * ci: move beautification to separate workflow @@ -2533,7 +2559,7 @@ Run isort and Black on pushes to master. Any edits made are committed. isort and * docs: include README.rst in index.rst -These files were very similar so it makes sense to simply include one +These files were very similar so it makes sense to simply include one inside the other. ([`8673a9d`](https://github.com/python-semantic-release/python-semantic-release/commit/8673a9d92a9bf348bb3409e002a830741396c8ca)) * docs: rewrite README.rst ([`e049772`](https://github.com/python-semantic-release/python-semantic-release/commit/e049772cf14cdd49538cf357db467f0bf3fe9587)) @@ -2563,12 +2589,12 @@ inside the other. ([`8673a9d`](https://github.com/python-semantic-release/python * ci: fetch full history in release job -I didn't realise that actions/checkout@v2 only fetches 1 commit by +I didn't realise that actions/checkout@v2 only fetches 1 commit by default. ([`a02a9b7`](https://github.com/python-semantic-release/python-semantic-release/commit/a02a9b7e34d8e7f8bb3b9c8aa1b5e1ef8bdd406c)) * ci: run tests on pull_request -The tests didn't run for #211 which caused a flake8 failure to be +The tests didn't run for #211 which caused a flake8 failure to be missed. ([`32fd77e`](https://github.com/python-semantic-release/python-semantic-release/commit/32fd77ed835bcfc943abeacec4e327df045b2ec9)) * ci: run tests on GitHub Actions ([`39ff283`](https://github.com/python-semantic-release/python-semantic-release/commit/39ff283312a0c686bfc5be71e1da9b6456652d95)) @@ -2591,7 +2617,7 @@ Automatically create pages in the API docs section using sphinx-autodoc. This is * style: fix styling from 2997908 -These code style problems were introduced because tests didn't run on +These code style problems were introduced because tests didn't run on #211. ([`172391e`](https://github.com/python-semantic-release/python-semantic-release/commit/172391ec5b5e490081b9b0ea58a94dfd5be33937)) @@ -2620,31 +2646,31 @@ These code style problems were introduced because tests didn't run on * fix: Bump dependencies and fix Windows issues on Development (#173) -* Bump dependencies and fix windows issues - -* Correctly pass temp dir to test settings - -* Remove print call on test settings - -* chore: remove py34 and py35 classifiers - -* chore: bump twine, requests and python-gitlab - -* chore: update tox config to be more granular - -* fix: missing mime types on Windows - -* chore: bump circleCI and tox python to 3.8 - -* chore: remove py36 from tox envlist - +* Bump dependencies and fix windows issues + +* Correctly pass temp dir to test settings + +* Remove print call on test settings + +* chore: remove py34 and py35 classifiers + +* chore: bump twine, requests and python-gitlab + +* chore: update tox config to be more granular + +* fix: missing mime types on Windows + +* chore: bump circleCI and tox python to 3.8 + +* chore: remove py36 from tox envlist + * chore: isort errors ([`0a6f8c3`](https://github.com/python-semantic-release/python-semantic-release/commit/0a6f8c3842b05f5f424dad5ce1fa5e3823c7e688)) ### Refactor * refactor(history): use a named tuple for parsed commits -This improves readability as we can use attributes such as 'bump' and +This improves readability as we can use attributes such as 'bump' and 'descriptions' instead of confusing numeric indices. ([`bff40d5`](https://github.com/python-semantic-release/python-semantic-release/commit/bff40d53174ffe27451d82132c31b112c7bee9fd)) @@ -2683,10 +2709,10 @@ will get the version they specify. ([`123984d`](https://github.com/python-semant * feat(build): allow config setting for build command (#195) -* feat(build): allow config setting for build command - -BREAKING CHANGE: Previously the build_commands configuration variable set the types of bundles sent to `python setup.py`. It has been replaced by the configuration variable `build_command` which takes the full command e.g. `python setup.py sdist` or `poetry build`. - +* feat(build): allow config setting for build command + +BREAKING CHANGE: Previously the build_commands configuration variable set the types of bundles sent to `python setup.py`. It has been replaced by the configuration variable `build_command` which takes the full command e.g. `python setup.py sdist` or `poetry build`. + Closes #188 ([`740f4bd`](https://github.com/python-semantic-release/python-semantic-release/commit/740f4bdb26569362acfc80f7e862fc2c750a46dd)) ### Fix @@ -2784,46 +2810,46 @@ Fixes #181 ([`0fddbe2`](https://github.com/python-semantic-release/python-semant * feat: Upload distribution files to GitHub Releases (#177) -* refactor(github): create upload_asset function - -Create a function to call the asset upload API. This will soon be used -to upload assets specified by the user. - -* refactor(github): infer Content-Type from file extension - -Infer the Content-Type header based on the file extension instead of setting it manually. - -* refactor(pypi): move building of dists to cli.py - -Refactor to have the building/removal of distributions in cli.py instead -of within the upload_to_pypi function. This makes way for uploading to -other locations, such as GitHub Releases, too. - -* feat(github): upload dists to release - -Upload Python wheels to the GitHub release. Configured with the option -upload_to_release, on by default if using GitHub. - -* docs: document upload_to_release config option - -* test(github): add tests for Github.upload_dists - -* fix(github): fix upload of .whl files - -Fix uploading of .whl files due to a missing MIME type (defined custom type as application/x-wheel+zip). Additionally, continue with other uploads even if one fails. - -* refactor(cli): additional output during publish - -Add some additional output during the publish command. - -* refactor(github): move api calls to separate methods - -Move each type of GitHub API request into its own method to improve readability. - -Re-implementation of #172 - -* fix: post changelog after PyPI upload - +* refactor(github): create upload_asset function + +Create a function to call the asset upload API. This will soon be used +to upload assets specified by the user. + +* refactor(github): infer Content-Type from file extension + +Infer the Content-Type header based on the file extension instead of setting it manually. + +* refactor(pypi): move building of dists to cli.py + +Refactor to have the building/removal of distributions in cli.py instead +of within the upload_to_pypi function. This makes way for uploading to +other locations, such as GitHub Releases, too. + +* feat(github): upload dists to release + +Upload Python wheels to the GitHub release. Configured with the option +upload_to_release, on by default if using GitHub. + +* docs: document upload_to_release config option + +* test(github): add tests for Github.upload_dists + +* fix(github): fix upload of .whl files + +Fix uploading of .whl files due to a missing MIME type (defined custom type as application/x-wheel+zip). Additionally, continue with other uploads even if one fails. + +* refactor(cli): additional output during publish + +Add some additional output during the publish command. + +* refactor(github): move api calls to separate methods + +Move each type of GitHub API request into its own method to improve readability. + +Re-implementation of #172 + +* fix: post changelog after PyPI upload + Post the changelog in-between uploading to PyPI and uploading to GitHub Releases. This is so that if the PyPI upload fails, GitHub users will not be notified. GitHub uploads still need to be processed after creating the changelog as the release notes must be published to upload assets to them. ([`e427658`](https://github.com/python-semantic-release/python-semantic-release/commit/e427658e33abf518191498c3142a0f18d3150e07)) ### Fix @@ -2843,7 +2869,7 @@ application/octet-stream is more generic, but it is better than using a non-offi * feat(history): capitalize changelog messages -Capitalize the first letter of messages in the changelog regardless of +Capitalize the first letter of messages in the changelog regardless of whether they are capitalized in the commit itself. ([`1a8e306`](https://github.com/python-semantic-release/python-semantic-release/commit/1a8e3060b8f6d6362c27903dcfc69d17db5f1d36)) ### Fix @@ -2881,7 +2907,7 @@ Fix the syntax of a broken bullet-point list in README.rst. ([`7aa572b`](https:/ * fix(github): send token in request header -Use an Authorization header instead of deprecated query parameter +Use an Authorization header instead of deprecated query parameter authorization. Fixes relekang/python-semantic-release#167 ([`be9972a`](https://github.com/python-semantic-release/python-semantic-release/commit/be9972a7b1fb183f738fb31bd370adb30281e4d5)) @@ -2952,13 +2978,13 @@ Fixes #156 ([`a4f8a10`](https://github.com/python-semantic-release/python-semant * fix: fallback to whole log if correct tag is not available (#157) -The method getting all commits to consider for the release will now test -whether the version in input is a valid reference. If it is not, it will -consider the whole log for the repository. - -evaluate_version_bump will still consider a message starting with the -version number as a breaking condition to stop analyzing. - +The method getting all commits to consider for the release will now test +whether the version in input is a valid reference. If it is not, it will +consider the whole log for the repository. + +evaluate_version_bump will still consider a message starting with the +version number as a breaking condition to stop analyzing. + Fixes #51 ([`252bffd`](https://github.com/python-semantic-release/python-semantic-release/commit/252bffd3be7b6dfcfdb384d24cb1cd83d990fc9a)) @@ -2968,14 +2994,14 @@ Fixes #51 ([`252bffd`](https://github.com/python-semantic-release/python-semanti * fix: Set version of click to >=2.0,<8.0. (#155) -* fix: Upgrade to click 7.0. - -Fixes #117 - -* fix: Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. - -* Upstream is at ~=7.0, so let's set the range to less than 8.0. - +* fix: Upgrade to click 7.0. + +Fixes #117 + +* fix: Instead of requiring click 7.0, looks like all tests will pass with at least 2.0. + +* Upstream is at ~=7.0, so let's set the range to less than 8.0. + * The string template has no variables, so remove the call to .format() ([`f07c7f6`](https://github.com/python-semantic-release/python-semantic-release/commit/f07c7f653be1c018e443f071d9a196d9293e9521)) @@ -3166,9 +3192,9 @@ The commands is lacking from the documentation. ([`b6fa04d`](https://github.com/ ### Refactor -* refactor: added debug to hvcshvcs - +* refactor: added debug to hvcshvcs + module did not have any debug ([`0c6237b`](https://github.com/python-semantic-release/python-semantic-release/commit/0c6237bc01ec39608fb768925091c755d9bb25bd)) * refactor: fix import sorting ([`01e4c5d`](https://github.com/python-semantic-release/python-semantic-release/commit/01e4c5d743f2f237d2c85481118e467d4f5fde15)) @@ -3192,14 +3218,14 @@ module did not have any debug ([`0c6237b`](https://github.com/python-semantic-re * fix: Maintain version variable formatting on bump (#103) -Small change to the way the version is written to the config file it is read from. This allows the formatting to be the same as before semantic-release changed it. - -Prior behavior -`my_version_var="1.2.3"` => `my_version_var = '1.2.4'` - -New behavior -`my_version_var="1.2.3"` => `my_version_var="1.2.4"` - +Small change to the way the version is written to the config file it is read from. This allows the formatting to be the same as before semantic-release changed it. + +Prior behavior +`my_version_var="1.2.3"` => `my_version_var = '1.2.4'` + +New behavior +`my_version_var="1.2.3"` => `my_version_var="1.2.4"` + I am using python-semantic-release with a Julia project and this change will allow for consistent formatting in my Project.toml file where the version is maintained ([`bf63156`](https://github.com/python-semantic-release/python-semantic-release/commit/bf63156f60340614fae94c255fb2f097cf317b2b)) * fix: initialize git Repo from current folder @@ -3328,8 +3354,8 @@ Closes #88 re #32 ([`8df5e2b`](https://github.com/python-semantic-release/pytho * fix: Change requests from fixed version to version range (#93) -* Change requests version to be more flexible to aid in using this with dev requirements for a release. - +* Change requests version to be more flexible to aid in using this with dev requirements for a release. + * revert changes to vcs helpers ([`af3ad59`](https://github.com/python-semantic-release/python-semantic-release/commit/af3ad59f018876e11cc3acdda0b149f8dd5606bd)) ### Refactor @@ -3401,21 +3427,21 @@ Change the Gitpython version number to fix a bug described in #80. ([`23c9d4b`]( * feat: Add support to finding previous version from tags if not using commit messages (#68) -* feat: Be a bit more forgiving to find previous tags - -Now grabs the previous version from tag names if it can't find it in the commit - -* quantifiedcode and flake8 fixes - -* Update cli.py - +* feat: Be a bit more forgiving to find previous tags + +Now grabs the previous version from tag names if it can't find it in the commit + +* quantifiedcode and flake8 fixes + +* Update cli.py + * Switch to ImproperConfigurationError ([`6786487`](https://github.com/python-semantic-release/python-semantic-release/commit/6786487ebf4ab481139ef9f43cd74e345debb334)) * feat: Add --retry cli option (#78) -* Add --retry cli option -* Post changelog correctly -* Add comments +* Add --retry cli option +* Post changelog correctly +* Add comments * Add --retry to the docs ([`3e312c0`](https://github.com/python-semantic-release/python-semantic-release/commit/3e312c0ce79a78d25016a3b294b772983cfb5e0f)) ### Fix @@ -3451,9 +3477,9 @@ Fixes #74 ([`1dc306b`](https://github.com/python-semantic-release/python-semanti * fix: error when not in git repository (#75) -Fix an error when the program was run in a non-git repository. It would -not allow the help options to be run. - +Fix an error when the program was run in a non-git repository. It would +not allow the help options to be run. + issue #74 ([`251b190`](https://github.com/python-semantic-release/python-semantic-release/commit/251b190a2fd5df68892346926d447cbc1b32475a)) ### Unknown @@ -3507,12 +3533,12 @@ This reverts commit 93e5507da6d53ecf63405507390633ef480c52fb. ([`195ed8d`](https * feat: Add git hash to the changelog (#65) -* feat(*): add git hash to the changelog - -Add git hash to the changelog to ease finding the specific commit. The hash now is also easily viewable in Github's tag. see #63 for more information. - -* chore(test_history): fix test errors - +* feat(*): add git hash to the changelog + +Add git hash to the changelog to ease finding the specific commit. The hash now is also easily viewable in Github's tag. see #63 for more information. + +* chore(test_history): fix test errors + Fix the test errors that would happen after the modification of get_commit_log. ([`628170e`](https://github.com/python-semantic-release/python-semantic-release/commit/628170ebc440fc6abf094dd3e393f40576dedf9b)) ### Fix @@ -4226,7 +4252,7 @@ parsed. ([`974ccda`](https://github.com/python-semantic-release/python-semantic- * Merge pull request #15 from jezdez/python-2 -Add Python 2.7 support. Fix #10. +Add Python 2.7 support. Fix #10. :sparkles: ([`5daabb7`](https://github.com/python-semantic-release/python-semantic-release/commit/5daabb75eb9145566a2a7c2a9e64439df7cd85f1)) * Add Python 2.7 support. Fix #10. ([`c05e13f`](https://github.com/python-semantic-release/python-semantic-release/commit/c05e13f22163237e963c493ffeda7e140f0202c6)) diff --git a/pyproject.toml b/pyproject.toml index 948f7052f..dfbde9464 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "8.7.1" +version = "8.7.2" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.7" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 931879fdf..78d348028 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "8.7.1" +__version__ = "8.7.2" def setup_hook(argv: list[str]) -> None: From ad086f5993ae4741d6e20fee618d1bce8df394fb Mon Sep 17 00:00:00 2001 From: bernardcooke53 <66492393+bernardcooke53@users.noreply.github.com> Date: Tue, 6 Feb 2024 20:34:52 +0000 Subject: [PATCH 040/167] fix!: drop support for Python 3.7 (#828) --- .github/workflows/main.yml | 2 +- .github/workflows/pr.yml | 2 +- .pre-commit-config.yaml | 2 +- pyproject.toml | 9 ++++----- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a83d152ca..2fa4a5784 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 914b10653..7a0259807 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e24beae3..2d72e0a4d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,7 +47,7 @@ repos: rev: v3.15.0 hooks: - id: pyupgrade - args: ["--py37-plus", "--keep-runtime-typing"] + args: ["--py38-plus", "--keep-runtime-typing"] # Linters and validation - repo: https://github.com/astral-sh/ruff-pre-commit diff --git a/pyproject.toml b/pyproject.toml index dfbde9464..753961dfa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,12 +8,11 @@ build-backend = "setuptools.build_meta" name = "python-semantic-release" version = "8.7.2" description = "Automatic Semantic Versioning for Python projects" -requires-python = ">=3.7" +requires-python = ">=3.8" license = { text = "MIT" } classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -103,7 +102,7 @@ legacy_tox_ini = """ [tox] envlist = mypy, - py{37,38,39,310,311,312}, + py{38,39,310,311,312}, coverage ruff skipsdist = True @@ -136,7 +135,7 @@ commands = """ [tool.mypy] -python_version = 3.7 +python_version = "3.8" packages = ["semantic_release"] show_column_numbers = true show_error_context = true @@ -258,7 +257,7 @@ ignore = [ ] external = ["V"] -target-version = "py37" +target-version = "py38" force-exclude = true line-length = 88 output-format = "grouped" From 279b680441b8f90708699d5f27316b61b1891f55 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Tue, 6 Feb 2024 20:42:10 +0000 Subject: [PATCH 041/167] 9.0.0 Automatically generated by python-semantic-release --- CHANGELOG.md | 7 +++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02a5da796..004a4df90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ +## v9.0.0 (2024-02-06) + +### Breaking + +* fix!: drop support for Python 3.7 (#828) ([`ad086f5`](https://github.com/python-semantic-release/python-semantic-release/commit/ad086f5993ae4741d6e20fee618d1bce8df394fb)) + + ## v8.7.2 (2024-01-03) ### Build diff --git a/pyproject.toml b/pyproject.toml index 753961dfa..50eb1e2dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "8.7.2" +version = "9.0.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 78d348028..66606986a 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "8.7.2" +__version__ = "9.0.0" def setup_hook(argv: list[str]) -> None: From 9c594fb6efac7e4df2b0bfbd749777d3126d03d7 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 6 Feb 2024 16:15:48 -0500 Subject: [PATCH 042/167] fix(config): set commit parser opt defaults based on parser choice (#782) --- docs/configuration.rst | 74 ++++++++++++++----- semantic_release/cli/config.py | 59 +++++++++------ semantic_release/commit_parser/_base.py | 2 +- .../unit/semantic_release/cli/test_config.py | 59 ++++++++++++++- tests/util.py | 19 ++++- 5 files changed, 168 insertions(+), 45 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 210c1c32a..5c7427057 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -207,27 +207,61 @@ or transformation. For more information, see :ref:`commit-parsing-parser-options`. -The default values are the defaults for :ref:`commit-parser-angular` - -**Default:** - -.. code-block:: toml +The default value for this setting depends on what you specify as +:ref:`commit_parser `. The table below outlines +the expections from ``commit_parser`` value to default options value. + +================== == ================================= +``commit_parser`` Default ``commit_parser_options`` +================== == ================================= +``"angular"`` -> .. code-block:: toml + + [tool.semantic_release.commit_parser_options] + allowed_types = [ + "build", "chore", "ci", "docs", "feat", "fix", + "perf", "style", "refactor", "test" + ] + minor_types = ["feat"] + patch_types = ["fix", "perf"] + +``"emoji"`` -> .. code-block:: toml + + [tool.semantic_release.commit_parser_options] + major_tags = [":boom:"] + minor_tags = [ + ":sparkles:", ":children_crossing:", ":lipstick:", + ":iphone:", ":egg:", ":chart_with_upwards_trend:" + ] + patch_tags = [ + ":ambulance:", ":lock:", ":bug:", ":zap:", ":goal_net:", + ":alien:", ":wheelchair:", ":speech_balloon:", ":mag:", + ":apple:", ":penguin:", ":checkered_flag:", ":robot:", + ":green_apple:" + ] + +``"scipy"`` -> .. code-block:: toml + + [tool.semantic_release.commit_parser_options] + allowed_tags = [ + "API", "DEP", "ENH", "REV", "BUG", "MAINT", "BENCH", + "BLD", "DEV", "DOC", "STY", "TST", "REL", "FEAT", "TEST", + ] + major_tags = ["API",] + minor_tags = ["DEP", "DEV", "ENH", "REV", "FEAT"] + patch_tags = ["BLD", "BUG", "MAINT"] + +``"tag"`` -> .. code-block:: toml + + [tool.semantic_release.commit_parser_options] + minor_tag = ":sparkles:" + patch_tag = ":nut_and_bolt:" + +``"module:class"`` -> ``**module:class.parser_options()`` +================== == ================================= + +**Default:** ``ParserOptions { ... }``, where ``...`` depends on +:ref:`commit_parser ` as indicated above. - [tool.semantic_release.commit_parser_options] - allowed_tags = [ - "build", - "chore", - "ci", - "docs", - "feat", - "fix", - "perf", - "style", - "refactor", - "test", - ] - minor_tags = ["feat"] - patch_tags = ["fix", "perf"] .. _config-logging-use-named-masks: diff --git a/semantic_release/cli/config.py b/semantic_release/cli/config.py index 383443f44..084cfc468 100644 --- a/semantic_release/cli/config.py +++ b/semantic_release/cli/config.py @@ -3,7 +3,8 @@ import logging import os import re -from dataclasses import dataclass +from collections.abc import Mapping +from dataclasses import dataclass, is_dataclass from enum import Enum from pathlib import Path from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type, Union @@ -11,8 +12,8 @@ from git import Actor from git.repo.base import Repo from jinja2 import Environment -from pydantic import BaseModel, model_validator -from typing_extensions import Literal +from pydantic import BaseModel, Field, RootModel, ValidationError, model_validator +from typing_extensions import Annotated, Literal from semantic_release import hvcs from semantic_release.changelog import environment @@ -38,6 +39,7 @@ ) log = logging.getLogger(__name__) +NonEmptyString = Annotated[str, Field(..., min_length=1)] class HvcsClient(str, Enum): @@ -46,7 +48,7 @@ class HvcsClient(str, Enum): GITEA = "gitea" -_known_commit_parsers = { +_known_commit_parsers: Dict[str, type[CommitParser]] = { "angular": AngularCommitParser, "emoji": EmojiCommitParser, "scipy": ScipyCommitParser, @@ -136,24 +138,9 @@ class RawConfig(BaseModel): env="GIT_COMMIT_AUTHOR", default=DEFAULT_COMMIT_AUTHOR ) commit_message: str = COMMIT_MESSAGE - commit_parser: str = "angular" + commit_parser: NonEmptyString = "angular" # It's up to the parser_options() method to validate these - commit_parser_options: Dict[str, Any] = { - "allowed_tags": [ - "build", - "chore", - "ci", - "docs", - "feat", - "fix", - "perf", - "style", - "refactor", - "test", - ], - "minor_tags": ["feat"], - "patch_tags": ["fix", "perf"], - } + commit_parser_options: Dict[str, Any] = {} logging_use_named_masks: bool = False major_on_zero: bool = True remote: RemoteConfig = RemoteConfig() @@ -162,6 +149,36 @@ class RawConfig(BaseModel): version_toml: Optional[Tuple[str, ...]] = None version_variables: Optional[Tuple[str, ...]] = None + @model_validator(mode="after") + def set_default_opts(self) -> RawConfig: + # Set the default parser options for the given commit parser when no user input is given + if not self.commit_parser_options and self.commit_parser: + parser_opts_type = None + # If the commit parser is a known one, pull the default options object from it + if self.commit_parser in _known_commit_parsers: + parser_opts_type = _known_commit_parsers[self.commit_parser].parser_options + else: + # if its a custom parser, try to import it and pull the default options object type + custom_class = dynamic_import(self.commit_parser) + if hasattr(custom_class, "parser_options"): + parser_opts_type = custom_class.parser_options + + # from either the custom opts class or the known parser opts class, create an instance + if callable(parser_opts_type): + opts_obj = parser_opts_type() + # if the opts object is a dataclass, wrap it in a RootModel so it can be transformed to a Mapping + opts_obj = opts_obj if not is_dataclass(opts_obj) else RootModel(opts_obj) + # Must be a mapping, so if it's a BaseModel, dump the model to a dict + self.commit_parser_options = ( + opts_obj.model_dump() + if isinstance(opts_obj, (BaseModel, RootModel)) + else opts_obj + ) + if not isinstance(self.commit_parser_options, Mapping): + raise ValidationError(f"Invalid parser options: {opts_obj}. Must be a mapping.") + + return self + @dataclass class GlobalCommandLineOptions: diff --git a/semantic_release/commit_parser/_base.py b/semantic_release/commit_parser/_base.py index 9c08ff26c..c41b66f61 100644 --- a/semantic_release/commit_parser/_base.py +++ b/semantic_release/commit_parser/_base.py @@ -9,7 +9,7 @@ from git.objects.commit import Commit -class ParserOptions: +class ParserOptions(dict): """ ParserOptions should accept the keyword arguments they are interested in from configuration and process them as desired, ultimately creating attributes diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index 0f81aad5c..a34efff01 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -5,7 +5,7 @@ import pytest import tomlkit -from pydantic import ValidationError +from pydantic import RootModel, ValidationError from semantic_release.cli.config import ( EnvConfigVar, @@ -14,7 +14,14 @@ RawConfig, RuntimeContext, ) +from semantic_release.commit_parser.angular import AngularParserOptions +from semantic_release.commit_parser.emoji import EmojiParserOptions +from semantic_release.commit_parser.scipy import ScipyParserOptions +from semantic_release.commit_parser.tag import TagParserOptions from semantic_release.const import DEFAULT_COMMIT_AUTHOR +from semantic_release.enums import LevelBump + +from tests.util import CustomParserOpts if TYPE_CHECKING: from typing import Any @@ -27,9 +34,10 @@ ({"type": HvcsClient.GITLAB.value}, EnvConfigVar(env="GITLAB_TOKEN")), ({"type": HvcsClient.GITEA.value}, EnvConfigVar(env="GITEA_TOKEN")), ({}, EnvConfigVar(env="GH_TOKEN")), # default not provided -> means Github + ({"type": HvcsClient.GITHUB.value, "token": {'env': "CUSTOM_TOKEN"}}, EnvConfigVar(env="CUSTOM_TOKEN")), ], ) -def test_load_hvcs_default_token(remote_config: dict[str, Any], expected_token): +def test_load_hvcs_default_token(remote_config: dict[str, Any], expected_token: EnvConfigVar): raw_config = RawConfig.model_validate( { "remote": remote_config, @@ -49,6 +57,53 @@ def test_invalid_hvcs_type(remote_config: dict[str, Any]): assert "remote.type" in str(excinfo.value) +@pytest.mark.parametrize( + "commit_parser, expected_parser_opts", + [ + (None, RootModel(AngularParserOptions()).model_dump()), # default not provided -> means angular + ("angular", RootModel(AngularParserOptions()).model_dump()), + ("emoji", RootModel(EmojiParserOptions()).model_dump()), + ("scipy", RootModel(ScipyParserOptions()).model_dump()), + ("tag", RootModel(TagParserOptions()).model_dump()), + ("tests.util:CustomParserWithNoOpts", {}), + ("tests.util:CustomParserWithOpts", RootModel(CustomParserOpts()).model_dump()), + ], +) +def test_load_default_parser_opts(commit_parser: str | None, expected_parser_opts: dict[str, Any]): + raw_config = RawConfig.model_validate( + # Since TOML does not support NoneTypes, we need to not include the key + { "commit_parser": commit_parser } if commit_parser else {} + ) + assert expected_parser_opts == raw_config.commit_parser_options + + +def test_load_user_defined_parser_opts(): + user_defined_opts = { + "allowed_tags": ["foo", "bar", "baz"], + "minor_tags": ["bar"], + "patch_tags": ["baz"], + "default_bump_level": LevelBump.PATCH.value, + } + raw_config = RawConfig.model_validate( + { + "commit_parser": "angular", + "commit_parser_options": user_defined_opts, + } + ) + assert user_defined_opts == raw_config.commit_parser_options + + +@pytest.mark.parametrize("commit_parser", [""]) +def test_invalid_commit_parser_value(commit_parser: str): + with pytest.raises(ValidationError) as excinfo: + RawConfig.model_validate( + { + "commit_parser": commit_parser, + } + ) + assert "commit_parser" in str(excinfo.value) + + def test_default_toml_config_valid(example_project): default_config_file = example_project / "default.toml" default_config_file.write_text( diff --git a/tests/util.py b/tests/util.py index 699e4873a..ad9780998 100644 --- a/tests/util.py +++ b/tests/util.py @@ -5,11 +5,15 @@ import string from contextlib import contextmanager from tempfile import NamedTemporaryFile -from typing import TYPE_CHECKING, Any, Iterable, TypeVar +from typing import TYPE_CHECKING, Any, Iterable, Tuple, TypeVar + +from pydantic.dataclasses import dataclass from semantic_release.changelog.context import make_changelog_context from semantic_release.changelog.release_history import ReleaseHistory from semantic_release.cli.commands import main +from semantic_release.commit_parser._base import CommitParser, ParserOptions +from semantic_release.commit_parser.token import ParseResult if TYPE_CHECKING: import filecmp @@ -136,3 +140,16 @@ def __getattr__(self, name: str) -> Any: for name, method in mocked_methods.items(): setattr(MockGitCommandWrapperType, f"mocked_{name}", method) return MockGitCommandWrapperType + + +class CustomParserWithNoOpts(CommitParser[ParseResult, ParserOptions]): + parser_options = ParserOptions + + +@dataclass +class CustomParserOpts(ParserOptions): + allowed_tags: Tuple[str, ...] = ("new", "custom") # noqa: UP006 + + +class CustomParserWithOpts(CommitParser[ParseResult, CustomParserOpts]): + parser_options = CustomParserOpts From 6ed24fe81ae2cf2a3823a1dff78552abbd9fd363 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 6 Feb 2024 21:21:04 +0000 Subject: [PATCH 043/167] style: beautify 9c594fb6efac7e4df2b0bfbd749777d3126d03d7 --- semantic_release/cli/config.py | 12 ++++++++--- .../unit/semantic_release/cli/test_config.py | 20 ++++++++++++++----- tests/util.py | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/semantic_release/cli/config.py b/semantic_release/cli/config.py index 084cfc468..2162f6633 100644 --- a/semantic_release/cli/config.py +++ b/semantic_release/cli/config.py @@ -156,7 +156,9 @@ def set_default_opts(self) -> RawConfig: parser_opts_type = None # If the commit parser is a known one, pull the default options object from it if self.commit_parser in _known_commit_parsers: - parser_opts_type = _known_commit_parsers[self.commit_parser].parser_options + parser_opts_type = _known_commit_parsers[ + self.commit_parser + ].parser_options else: # if its a custom parser, try to import it and pull the default options object type custom_class = dynamic_import(self.commit_parser) @@ -167,7 +169,9 @@ def set_default_opts(self) -> RawConfig: if callable(parser_opts_type): opts_obj = parser_opts_type() # if the opts object is a dataclass, wrap it in a RootModel so it can be transformed to a Mapping - opts_obj = opts_obj if not is_dataclass(opts_obj) else RootModel(opts_obj) + opts_obj = ( + opts_obj if not is_dataclass(opts_obj) else RootModel(opts_obj) + ) # Must be a mapping, so if it's a BaseModel, dump the model to a dict self.commit_parser_options = ( opts_obj.model_dump() @@ -175,7 +179,9 @@ def set_default_opts(self) -> RawConfig: else opts_obj ) if not isinstance(self.commit_parser_options, Mapping): - raise ValidationError(f"Invalid parser options: {opts_obj}. Must be a mapping.") + raise ValidationError( + f"Invalid parser options: {opts_obj}. Must be a mapping." + ) return self diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index a34efff01..88ec213b5 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -34,10 +34,15 @@ ({"type": HvcsClient.GITLAB.value}, EnvConfigVar(env="GITLAB_TOKEN")), ({"type": HvcsClient.GITEA.value}, EnvConfigVar(env="GITEA_TOKEN")), ({}, EnvConfigVar(env="GH_TOKEN")), # default not provided -> means Github - ({"type": HvcsClient.GITHUB.value, "token": {'env': "CUSTOM_TOKEN"}}, EnvConfigVar(env="CUSTOM_TOKEN")), + ( + {"type": HvcsClient.GITHUB.value, "token": {"env": "CUSTOM_TOKEN"}}, + EnvConfigVar(env="CUSTOM_TOKEN"), + ), ], ) -def test_load_hvcs_default_token(remote_config: dict[str, Any], expected_token: EnvConfigVar): +def test_load_hvcs_default_token( + remote_config: dict[str, Any], expected_token: EnvConfigVar +): raw_config = RawConfig.model_validate( { "remote": remote_config, @@ -60,7 +65,10 @@ def test_invalid_hvcs_type(remote_config: dict[str, Any]): @pytest.mark.parametrize( "commit_parser, expected_parser_opts", [ - (None, RootModel(AngularParserOptions()).model_dump()), # default not provided -> means angular + ( + None, + RootModel(AngularParserOptions()).model_dump(), + ), # default not provided -> means angular ("angular", RootModel(AngularParserOptions()).model_dump()), ("emoji", RootModel(EmojiParserOptions()).model_dump()), ("scipy", RootModel(ScipyParserOptions()).model_dump()), @@ -69,10 +77,12 @@ def test_invalid_hvcs_type(remote_config: dict[str, Any]): ("tests.util:CustomParserWithOpts", RootModel(CustomParserOpts()).model_dump()), ], ) -def test_load_default_parser_opts(commit_parser: str | None, expected_parser_opts: dict[str, Any]): +def test_load_default_parser_opts( + commit_parser: str | None, expected_parser_opts: dict[str, Any] +): raw_config = RawConfig.model_validate( # Since TOML does not support NoneTypes, we need to not include the key - { "commit_parser": commit_parser } if commit_parser else {} + {"commit_parser": commit_parser} if commit_parser else {} ) assert expected_parser_opts == raw_config.commit_parser_options diff --git a/tests/util.py b/tests/util.py index ad9780998..72db9a2b0 100644 --- a/tests/util.py +++ b/tests/util.py @@ -148,7 +148,7 @@ class CustomParserWithNoOpts(CommitParser[ParseResult, ParserOptions]): @dataclass class CustomParserOpts(ParserOptions): - allowed_tags: Tuple[str, ...] = ("new", "custom") # noqa: UP006 + allowed_tags: Tuple[str, ...] = ("new", "custom") # noqa: UP006 class CustomParserWithOpts(CommitParser[ParseResult, CustomParserOpts]): From 07a06cca1feb503bee3830b81e8d603ee0324cfa Mon Sep 17 00:00:00 2001 From: semantic-release Date: Tue, 6 Feb 2024 21:23:04 +0000 Subject: [PATCH 044/167] 9.0.1 Automatically generated by python-semantic-release --- CHANGELOG.md | 11 +++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 004a4df90..8011d81d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ +## v9.0.1 (2024-02-06) + +### Fix + +* fix(config): set commit parser opt defaults based on parser choice (#782) ([`9c594fb`](https://github.com/python-semantic-release/python-semantic-release/commit/9c594fb6efac7e4df2b0bfbd749777d3126d03d7)) + +### Style + +* style: beautify 9c594fb6efac7e4df2b0bfbd749777d3126d03d7 ([`6ed24fe`](https://github.com/python-semantic-release/python-semantic-release/commit/6ed24fe81ae2cf2a3823a1dff78552abbd9fd363)) + + ## v9.0.0 (2024-02-06) ### Breaking diff --git a/pyproject.toml b/pyproject.toml index 50eb1e2dd..bbe3772ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.0.0" +version = "9.0.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 66606986a..cb3d7bdeb 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "9.0.0" +__version__ = "9.0.1" def setup_hook(argv: list[str]) -> None: From e23845226120b4fb934dd8755ce1b3f822cac041 Mon Sep 17 00:00:00 2001 From: Heinz-Alexander Fuetterer <35225576+afuetterer@users.noreply.github.com> Date: Tue, 6 Feb 2024 22:34:29 +0100 Subject: [PATCH 045/167] chore: update pre-commit hooks (#796) --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2d72e0a4d..f3882b75a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,7 +31,7 @@ repos: # Formatters that may modify source files automatically - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.2 + rev: v0.1.11 hooks: - id: ruff-format name: ruff (format) @@ -51,7 +51,7 @@ repos: # Linters and validation - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.2 + rev: v0.1.11 hooks: - id: ruff name: ruff (lint) @@ -64,7 +64,7 @@ repos: pass_filenames: false - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.6.1" + rev: "v1.8.0" hooks: - id: mypy args: ["--config-file", "pyproject.toml"] @@ -100,7 +100,7 @@ repos: - "tests" - repo: https://github.com/pycqa/bandit - rev: 1.7.5 + rev: 1.7.6 hooks: - id: bandit args: @@ -114,7 +114,7 @@ repos: # GHA linting - repo: https://github.com/python-jsonschema/check-jsonschema - rev: "0.27.0" + rev: "0.27.3" hooks: - id: check-github-workflows - id: check-readthedocs From fb6f243a141642c02469f1080180ecaf4f3cec66 Mon Sep 17 00:00:00 2001 From: Tyler D Date: Tue, 6 Feb 2024 13:37:25 -0800 Subject: [PATCH 046/167] docs: Remove duplicate note in configuration.rst (#807) --- docs/configuration.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 5c7427057..1ff72fb5a 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -100,11 +100,6 @@ In this structure: Settings -------- -.. note:: - If you are using the built-in GitHub Action, the default value is set to - ``github-actions ``. You can modify this with the - ``git_committer_name`` and ``git_committer_email`` inputs. - .. _config-root: ``[tool.semantic_release]`` From 3eb15c413cec0430fe2b27a313185068f900c61d Mon Sep 17 00:00:00 2001 From: Heinz-Alexander Fuetterer <35225576+afuetterer@users.noreply.github.com> Date: Tue, 6 Feb 2024 22:38:53 +0100 Subject: [PATCH 047/167] ci: add grouped github-actions section to dependabot config (#794) --- .github/dependabot.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index ff5c78759..07d648f87 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -14,3 +14,18 @@ updates: - dependencies open-pull-requests-limit: 10 rebase-strategy: auto + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "18:00" + commit-message: + prefix: "ci" + labels: + - dependencies + rebase-strategy: auto + groups: + github-actions: + patterns: + - "*" From 229c6471efc2c1bee002c3b89f58caf391b89e78 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 6 Feb 2024 16:51:06 -0500 Subject: [PATCH 048/167] test(fixtures): cache the base example project directory (#799) --- tests/command_line/conftest.py | 4 +- tests/command_line/test_changelog.py | 20 +- tests/command_line/test_version.py | 32 +-- tests/conftest.py | 37 +++ tests/fixtures/example_project.py | 223 ++++++++++++------ tests/fixtures/git_repo.py | 6 +- tests/scenario/test_template_render.py | 39 +-- .../unit/semantic_release/cli/test_config.py | 11 +- .../unit/semantic_release/hvcs/test_gitea.py | 18 +- .../unit/semantic_release/hvcs/test_github.py | 18 +- .../version/test_declaration.py | 7 +- 11 files changed, 285 insertions(+), 130 deletions(-) diff --git a/tests/command_line/conftest.py b/tests/command_line/conftest.py index b9c666469..823cf9e59 100644 --- a/tests/command_line/conftest.py +++ b/tests/command_line/conftest.py @@ -53,8 +53,8 @@ def mocked_git_push(monkeypatch: MonkeyPatch) -> MagicMock: @pytest.fixture -def config_path(example_project: Path) -> Path: - return example_project / DEFAULT_CONFIG_FILE +def config_path(example_project_dir: Path) -> Path: + return example_project_dir / DEFAULT_CONFIG_FILE @pytest.fixture diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 045e4fb99..c94c76eca 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -57,13 +57,13 @@ def test_changelog_noop_is_noop( tag: str | None, arg0: str | None, tmp_path_factory: pytest.TempPathFactory, - example_project: ExProjectDir, + example_project_dir: ExProjectDir, cli_runner: CliRunner, ): args = [arg0, tag] if tag and arg0 else [] tempdir = tmp_path_factory.mktemp("test_noop") shutil.rmtree(str(tempdir.resolve())) - shutil.copytree(src=str(example_project.resolve()), dst=tempdir) + shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) # Set up a requests HTTP session so we can catch the HTTP calls and ensure # they're made @@ -88,7 +88,7 @@ def test_changelog_noop_is_noop( assert result.exit_code == 0 - dcmp = filecmp.dircmp(str(example_project.resolve()), tempdir) + dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) differing_files = flatten_dircmp(dcmp) assert not differing_files @@ -112,13 +112,13 @@ def test_changelog_noop_is_noop( def test_changelog_content_regenerated( repo: Repo, tmp_path_factory: pytest.TempPathFactory, - example_project: ExProjectDir, + example_project_dir: ExProjectDir, example_changelog_md: Path, cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_changelog") shutil.rmtree(str(tempdir.resolve())) - shutil.copytree(src=str(example_project.resolve()), dst=tempdir) + shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) # Remove the changelog and then check that we can regenerate it os.remove(str(example_changelog_md.resolve())) @@ -126,7 +126,7 @@ def test_changelog_content_regenerated( result = cli_runner.invoke(main, [changelog.name or "changelog"]) assert result.exit_code == 0 - dcmp = filecmp.dircmp(str(example_project.resolve()), tempdir) + dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) differing_files = flatten_dircmp(dcmp) assert not differing_files @@ -140,12 +140,12 @@ def test_changelog_content_regenerated( def test_changelog_release_tag_not_in_history( args: list[str], tmp_path_factory: pytest.TempPathFactory, - example_project: ExProjectDir, + example_project_dir: ExProjectDir, cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_changelog") shutil.rmtree(str(tempdir.resolve())) - shutil.copytree(src=str(example_project.resolve()), dst=tempdir) + shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) result = cli_runner.invoke(main, [changelog.name or "changelog", *args]) assert result.exit_code == 2 @@ -158,12 +158,12 @@ def test_changelog_post_to_release( args: list[str], monkeypatch: pytest.MonkeyPatch, tmp_path_factory: pytest.TempPathFactory, - example_project: ExProjectDir, + example_project_dir: ExProjectDir, cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_changelog") shutil.rmtree(str(tempdir.resolve())) - shutil.copytree(src=str(example_project.resolve()), dst=tempdir) + shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) # Set up a requests HTTP session so we can catch the HTTP calls and ensure they're # made diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 58a903f8b..da5c49115 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -45,10 +45,10 @@ lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], ) -def test_version_noop_is_noop(tmp_path_factory, example_project, repo, cli_runner): +def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_runner): # Make a commit to ensure we have something to release # otherwise the "no release will be made" logic will kick in first - new_file = example_project / "temp.txt" + new_file = example_project_dir / "temp.txt" new_file.write_text("noop version test") repo.git.add(str(new_file.resolve())) @@ -56,7 +56,7 @@ def test_version_noop_is_noop(tmp_path_factory, example_project, repo, cli_runne tempdir = tmp_path_factory.mktemp("test_noop") shutil.rmtree(str(tempdir.resolve())) - shutil.copytree(src=str(example_project.resolve()), dst=tempdir) + shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) @@ -67,7 +67,7 @@ def test_version_noop_is_noop(tmp_path_factory, example_project, repo, cli_runne head_after = repo.head.commit assert result.exit_code == 0 - dcmp = filecmp.dircmp(str(example_project.resolve()), tempdir) + dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) differing_files = flatten_dircmp(dcmp) assert not differing_files @@ -210,11 +210,11 @@ def test_version_noop_is_noop(tmp_path_factory, example_project, repo, cli_runne ], ) def test_version_print( - repo, cli_args, expected_stdout, example_project, tmp_path_factory, cli_runner + repo, cli_args, expected_stdout, example_project_dir, tmp_path_factory, cli_runner ): # Make a commit to ensure we have something to release # otherwise the "no release will be made" logic will kick in first - new_file = example_project / "temp.txt" + new_file = example_project_dir / "temp.txt" new_file.write_text("noop version test") repo.git.add(str(new_file.resolve())) @@ -222,7 +222,7 @@ def test_version_print( tempdir = tmp_path_factory.mktemp("test_version_print") shutil.rmtree(str(tempdir.resolve())) - shutil.copytree(src=str(example_project.resolve()), dst=tempdir) + shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) @@ -235,7 +235,7 @@ def test_version_print( assert tags_before == tags_after assert head_before == head_after assert result.stdout.rstrip("\n") == expected_stdout - dcmp = filecmp.dircmp(str(example_project.resolve()), tempdir) + dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) differing_files = flatten_dircmp(dcmp) assert not differing_files @@ -390,14 +390,14 @@ def test_version_no_push_force_level( repo: Repo, cli_args: list[str], expected_new_version: str, - example_project: ExProjectDir, + example_project_dir: ExProjectDir, example_pyproject_toml: Path, tmp_path_factory: pytest.TempPathFactory, cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_version") shutil.rmtree(str(tempdir.resolve())) - shutil.copytree(src=str(example_project.resolve()), dst=tempdir) + shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) @@ -414,7 +414,7 @@ def test_version_no_push_force_level( assert head_before != head_after # A commit has been made assert head_before in repo.head.commit.parents - dcmp = filecmp.dircmp(str(example_project.resolve()), tempdir) + dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) differing_files = flatten_dircmp(dcmp) # Changelog already reflects changes this should introduce @@ -441,7 +441,7 @@ def test_version_no_push_force_level( # Compare _version.py new_init_py = ( - (example_project / "src" / EXAMPLE_PROJECT_NAME / "_version.py") + (example_project_dir / "src" / EXAMPLE_PROJECT_NAME / "_version.py") .read_text(encoding="utf-8") .splitlines(keepends=True) ) @@ -641,13 +641,13 @@ def test_version_only_update_files_no_git_actions( cli_runner: CliRunner, tmp_path_factory: pytest.TempPathFactory, example_pyproject_toml: Path, - example_project: ExProjectDir, + example_project_dir: ExProjectDir, ) -> None: # Arrange expected_new_version = "0.3.0" tempdir = tmp_path_factory.mktemp("test_version") shutil.rmtree(str(tempdir.resolve())) - shutil.copytree(src=str(example_project), dst=tempdir) + shutil.copytree(src=str(example_project_dir), dst=tempdir) head_before = runtime_context_with_tags.repo.head.commit tags_before = runtime_context_with_tags.repo.tags @@ -670,7 +670,7 @@ def test_version_only_update_files_no_git_actions( f"'semantic-release {str.join(' ', args)}': " + resp.stderr ) - dcmp = filecmp.dircmp(str(example_project.resolve()), tempdir) + dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) differing_files = flatten_dircmp(dcmp) # Files that should receive version change @@ -697,7 +697,7 @@ def test_version_only_update_files_no_git_actions( # Compare _version.py new_version_py = ( - (example_project / "src" / EXAMPLE_PROJECT_NAME / "_version.py") + (example_project_dir / "src" / EXAMPLE_PROJECT_NAME / "_version.py") .read_text(encoding="utf-8") .splitlines(keepends=True) ) diff --git a/tests/conftest.py b/tests/conftest.py index 1c7f203b4..9ef49b72b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,2 +1,39 @@ """Note: fixtures are stored in the tests/fixtures directory for better organisation""" +from __future__ import annotations + +import shutil +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest + from tests.fixtures import * + +if TYPE_CHECKING: + from typing import Generator, Protocol + + class TeardownCachedDirFn(Protocol): + def __call__(self, directory: Path) -> Path: + ... + + +@pytest.fixture(scope="session") +def cached_files_dir(tmp_path_factory: pytest.TempPathFactory) -> Path: + return tmp_path_factory.mktemp("cached_files_dir") + + +@pytest.fixture(scope="session") +def teardown_cached_dir() -> Generator[TeardownCachedDirFn, None, None]: + directories: list[Path] = [] + + def _teardown_cached_dir(directory: Path | str) -> Path: + directories.append(Path(directory)) + return directories[-1] + + try: + yield _teardown_cached_dir + finally: + # clean up any registered cached directories + for directory in directories: + if directory.exists(): + shutil.rmtree(str(directory)) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 71ac6f5bb..8d6c21459 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -1,6 +1,8 @@ from __future__ import annotations import os +import shutil +import sys from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING, Generator @@ -32,6 +34,8 @@ from semantic_release.commit_parser import CommitParser from semantic_release.hvcs import HvcsBase + from tests.conftest import TeardownCachedDirFn + ExProjectDir = Path class UpdatePyprojectTomlFn(Protocol): @@ -47,108 +51,187 @@ def __call__(self) -> type[CommitParser]: ... +@pytest.fixture(scope="session") +def pyproject_toml_file() -> Path: + return Path("pyproject.toml") + + +@pytest.fixture(scope="session") +def setup_cfg_file() -> Path: + return Path("setup.cfg") + + +@pytest.fixture(scope="session") +def setup_py_file() -> Path: + return Path("setup.py") + + +@pytest.fixture(scope="session") +def changelog_md_file() -> Path: + return Path("CHANGELOG.md") + + +@pytest.fixture(scope="session") +def changelog_template_dir() -> Path: + return Path("templates") + + +@pytest.fixture +def example_project_dir(tmp_path: Path) -> ExProjectDir: + return tmp_path.resolve() + + + @pytest.fixture -def change_to_tmp_dir(tmp_path: Path) -> Generator[Path, None, None]: +def change_to_ex_proj_dir(example_project_dir: ExProjectDir) -> Generator[None, None, None]: cwd = os.getcwd() - os.chdir(str(tmp_path.resolve())) + tgt_dir = str(example_project_dir.resolve()) + if cwd == tgt_dir: + return + + os.chdir(tgt_dir) try: - yield Path(os.getcwd()) + yield finally: os.chdir(cwd) -@pytest.fixture -def example_project( - change_to_tmp_dir: Path, # noqa: U100 # must be given as an argument -) -> ExProjectDir: - tmp_path = change_to_tmp_dir - src_dir = tmp_path / "src" - src_dir.mkdir() - example_dir = src_dir / EXAMPLE_PROJECT_NAME - example_dir.mkdir() - init_py = example_dir / "__init__.py" - init_py.write_text( - dedent( - ''' - """ - An example package with a very informative docstring - """ - from ._version import __version__ - - - def hello_world() -> None: - print("Hello World") - ''' - ) - ) +@pytest.fixture(scope="session") +def cached_example_project( + pyproject_toml_file: Path, + setup_cfg_file: Path, + setup_py_file: Path, + changelog_md_file: Path, + cached_files_dir: Path, + changelog_template_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + """ + Initializes the example project. DO NOT USE DIRECTLY + + Use the `init_example_project` fixture instead. + """ + cached_project_path = (cached_files_dir / "example_project").resolve() + # purposefully a relative path + example_dir = Path("src", EXAMPLE_PROJECT_NAME) version_py = example_dir / "_version.py" - version_py.write_text( - dedent( - f""" - __version__ = "{EXAMPLE_PROJECT_VERSION}" - """ + gitignore_contents = dedent( + f""" + *.pyc + /src/**/{version_py.name} + """ + ).lstrip() + init_py_contents = dedent( + ''' + """ + An example package with a very informative docstring + """ + from ._version import __version__ + + + def hello_world() -> None: + print("Hello World") + ''' + ).lstrip() + version_py_contents = dedent( + f""" + __version__ = "{EXAMPLE_PROJECT_VERSION}" + """ + ).lstrip() + + for file, contents in [ + (example_dir / "__init__.py", init_py_contents), + (version_py, version_py_contents), + (".gitignore", gitignore_contents), + (pyproject_toml_file, EXAMPLE_PYPROJECT_TOML_CONTENT), + (setup_cfg_file, EXAMPLE_SETUP_CFG_CONTENT), + (setup_py_file, EXAMPLE_SETUP_PY_CONTENT), + (changelog_md_file, EXAMPLE_CHANGELOG_MD_CONTENT), + ]: + abs_filepath = cached_project_path.joinpath(file).resolve() + # make sure the parent directory exists + abs_filepath.parent.mkdir(parents=True, exist_ok=True) + # write file contents + abs_filepath.write_text(contents) + + # create the changelog template directory + cached_project_path.joinpath(changelog_template_dir).mkdir(parents=True, exist_ok=True) + + # trigger automatic cleanup of cache directory during teardown + return teardown_cached_dir(cached_project_path) + + +@pytest.fixture +def init_example_project( + example_project_dir: ExProjectDir, + cached_example_project: Path, + change_to_ex_proj_dir: None, +) -> None: + """This fixture initializes the example project in the current test's project directory.""" + if not cached_example_project.exists(): + raise RuntimeError( + f"Unable to find cached project files for {EXAMPLE_PROJECT_NAME}" ) - ) - gitignore = tmp_path / ".gitignore" - gitignore.write_text( - dedent( - f""" - *.pyc - /src/**/{version_py.name} - """ + + # Copy the cached project files into the current test's project directory + if sys.version_info[:2] == (3, 7): + # For 3.7 compatibility, destination can't exist, and dirs_exist_ok isn't available + # since destination had to be removed, handle changing directories to prevent error + os.chdir(str(example_project_dir.parent)) + shutil.rmtree(str(example_project_dir), ignore_errors=True) + shutil.copytree( + src=str(cached_example_project), + dst=str(example_project_dir), ) + os.chdir(str(example_project_dir)) + return + + shutil.copytree( + src=str(cached_example_project), + dst=str(example_project_dir), + dirs_exist_ok=True, ) - pyproject_toml = tmp_path / "pyproject.toml" - pyproject_toml.write_text(EXAMPLE_PYPROJECT_TOML_CONTENT) - setup_cfg = tmp_path / "setup.cfg" - setup_cfg.write_text(EXAMPLE_SETUP_CFG_CONTENT) - setup_py = tmp_path / "setup.py" - setup_py.write_text(EXAMPLE_SETUP_PY_CONTENT) - template_dir = tmp_path / "templates" - template_dir.mkdir() - changelog_md = tmp_path / "CHANGELOG.md" - changelog_md.write_text(EXAMPLE_CHANGELOG_MD_CONTENT) - return tmp_path - - -@pytest.fixture -def example_project_with_release_notes_template(example_project: Path) -> Path: - template_dir = example_project / "templates" + + +@pytest.fixture +def example_project_with_release_notes_template( + init_example_project: None, + example_project_dir: Path, +) -> Path: + template_dir = example_project_dir / "templates" release_notes_j2 = template_dir / ".release_notes.md.j2" release_notes_j2.write_text(EXAMPLE_RELEASE_NOTES_TEMPLATE) - return example_project + return example_project_dir @pytest.fixture -def example_pyproject_toml(example_project: ExProjectDir) -> Path: - return example_project / "pyproject.toml" +def example_pyproject_toml(example_project_dir: ExProjectDir) -> Path: + return example_project_dir / "pyproject.toml" @pytest.fixture -def example_setup_cfg(example_project: ExProjectDir) -> Path: - return example_project / "setup.cfg" +def example_setup_cfg(example_project_dir: ExProjectDir) -> Path: + return example_project_dir / "setup.cfg" @pytest.fixture -def example_setup_py(example_project: ExProjectDir) -> Path: - return example_project / "setup.py" +def example_setup_py(example_project_dir: ExProjectDir) -> Path: + return example_project_dir / "setup.py" # Note this is just the path and the content may change @pytest.fixture -def example_changelog_md(example_project: ExProjectDir) -> Path: - return example_project / "CHANGELOG.md" +def example_changelog_md(example_project_dir: ExProjectDir) -> Path: + return example_project_dir / "CHANGELOG.md" @pytest.fixture -def example_project_template_dir(example_project: ExProjectDir) -> Path: - return example_project / "templates" +def example_project_template_dir(example_project_dir: ExProjectDir) -> Path: + return example_project_dir / "templates" @pytest.fixture -def update_pyproject_toml( - example_project: Path, example_pyproject_toml: Path -) -> UpdatePyprojectTomlFn: +def update_pyproject_toml(example_pyproject_toml: Path) -> UpdatePyprojectTomlFn: """Update the pyproject.toml file with the given content.""" def _update_pyproject_toml(setting: str, value: Any) -> None: diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index e1338c997..7080fd894 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -49,12 +49,14 @@ def example_git_https_url(): params=[lazy_fixture("example_git_ssh_url")] ) def git_repo_factory( - request: pytest.FixtureRequest, example_project: ExProjectDir + request: pytest.FixtureRequest, + init_example_project: None, + example_project_dir: ExProjectDir, ) -> Generator[RepoInitFn, None, None]: repos: list[Repo] = [] def git_repo() -> Repo: - repo = Repo.init(example_project.resolve()) + repo = Repo.init(example_project_dir.resolve()) # store the repo so we can close it later repos.append(repo) diff --git a/tests/scenario/test_template_render.py b/tests/scenario/test_template_render.py index 86cee6739..af116a74f 100644 --- a/tests/scenario/test_template_render.py +++ b/tests/scenario/test_template_render.py @@ -83,8 +83,9 @@ def excluded_file(directory_path_with_hidden_subfolder): @pytest.mark.usefixtures("excluded_file") def test_recursive_render( - example_project, - example_project_template_dir, + init_example_project: None, + example_project_dir: Path, + example_project_template_dir: Path, normal_template, deeply_nested_file, hidden_file, @@ -92,47 +93,47 @@ def test_recursive_render( tmpl_dir = str(example_project_template_dir.resolve()) env = environment(template_dir=tmpl_dir) - preexisting_paths = set(example_project.rglob("**/*")) + preexisting_paths = set(example_project_dir.rglob("**/*")) recursive_render( template_dir=example_project_template_dir.resolve(), environment=env, - _root_dir=str(example_project.resolve()), + _root_dir=str(example_project_dir.resolve()), ) rendered_normal_template = _strip_trailing_j2( - example_project / normal_template.relative_to(example_project_template_dir) + example_project_dir / normal_template.relative_to(example_project_template_dir) ) assert rendered_normal_template.exists() assert rendered_normal_template.read_text() == NORMAL_TEMPLATE_RENDERED - rendered_deeply_nested = example_project / deeply_nested_file.relative_to( + rendered_deeply_nested = example_project_dir / deeply_nested_file.relative_to( example_project_template_dir ) assert rendered_deeply_nested.exists() assert rendered_deeply_nested.read_text() == PLAINTEXT_FILE_CONTENT - rendered_hidden = example_project / hidden_file.relative_to( + rendered_hidden = example_project_dir / hidden_file.relative_to( example_project_template_dir ) assert not rendered_hidden.exists() - assert not (example_project / "path").exists() + assert not (example_project_dir / "path").exists() - assert set(example_project.rglob("**/*")) == preexisting_paths.union( - example_project / p + assert set(example_project_dir.rglob("**/*")) == preexisting_paths.union( + example_project_dir / p for t in ( rendered_normal_template, rendered_deeply_nested, ) for p in itertools.accumulate( - t.relative_to(example_project).parts, func=lambda *a: os.sep.join(a) + t.relative_to(example_project_dir).parts, func=lambda *a: os.sep.join(a) ) ) @pytest.fixture -def dotfolder_template_dir(example_project: Path): - newpath = example_project / ".templates/.psr-templates" +def dotfolder_template_dir(example_project_dir: Path): + newpath = example_project_dir / ".templates/.psr-templates" newpath.mkdir(parents=True, exist_ok=True) return newpath @@ -145,20 +146,20 @@ def dotfolder_template(dotfolder_template_dir: Path): def test_recursive_render_with_top_level_dotfolder( - example_project, dotfolder_template, dotfolder_template_dir + example_project_dir, dotfolder_template, dotfolder_template_dir ): - preexisting_paths = set(example_project.rglob("**/*")) + preexisting_paths = set(example_project_dir.rglob("**/*")) env = environment(template_dir=dotfolder_template_dir.resolve()) recursive_render( template_dir=dotfolder_template_dir.resolve(), environment=env, - _root_dir=example_project.resolve(), + _root_dir=example_project_dir.resolve(), ) - rendered_template = example_project / dotfolder_template.name + rendered_template = example_project_dir / dotfolder_template.name assert rendered_template.exists() - assert set(example_project.rglob("**/*")) == preexisting_paths.union( - {example_project / rendered_template} + assert set(example_project_dir.rglob("**/*")) == preexisting_paths.union( + {example_project_dir / rendered_template} ) diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index 88ec213b5..aac47b18c 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -24,6 +24,7 @@ from tests.util import CustomParserOpts if TYPE_CHECKING: + from pathlib import Path from typing import Any @@ -114,8 +115,9 @@ def test_invalid_commit_parser_value(commit_parser: str): assert "commit_parser" in str(excinfo.value) -def test_default_toml_config_valid(example_project): - default_config_file = example_project / "default.toml" +def test_default_toml_config_valid(example_project_dir): + default_config_file = example_project_dir / "default.toml" + default_config_file.write_text( tomlkit.dumps(RawConfig().model_dump(mode="json", exclude_none=True)) ) @@ -139,10 +141,9 @@ def test_default_toml_config_valid(example_project): ], ) def test_commit_author_configurable( - example_project, repo_with_no_tags_angular_commits, mock_env, expected_author + example_pyproject_toml: Path, repo_with_no_tags_angular_commits, mock_env, expected_author ): - pyproject_toml = example_project / "pyproject.toml" - content = tomlkit.loads(pyproject_toml.read_text(encoding="utf-8")).unwrap() + content = tomlkit.loads(example_pyproject_toml.read_text(encoding="utf-8")).unwrap() with mock.patch.dict("os.environ", mock_env): raw = RawConfig.model_validate(content) diff --git a/tests/unit/semantic_release/hvcs/test_gitea.py b/tests/unit/semantic_release/hvcs/test_gitea.py index ef293ff36..9c47d7550 100644 --- a/tests/unit/semantic_release/hvcs/test_gitea.py +++ b/tests/unit/semantic_release/hvcs/test_gitea.py @@ -1,7 +1,10 @@ +from __future__ import annotations + import base64 import glob import os import re +from typing import TYPE_CHECKING from unittest import mock from urllib.parse import urlencode @@ -15,6 +18,9 @@ from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, RELEASE_NOTES from tests.util import netrc_file +if TYPE_CHECKING: + from pathlib import Path + @pytest.fixture def default_gitea_client(): @@ -508,7 +514,11 @@ def test_create_or_update_release_when_create_fails_and_no_release_for_tag( @pytest.mark.parametrize("status_code", (200, 201)) @pytest.mark.parametrize("mock_release_id", range(3)) def test_upload_asset_succeeds( - default_gitea_client, example_changelog_md, status_code, mock_release_id + init_example_project: None, + default_gitea_client: Gitea, + example_changelog_md: Path, + status_code: int, + mock_release_id: int, ): urlparams = {"name": example_changelog_md.name} with requests_mock.Mocker(session=default_gitea_client.session) as m: @@ -539,7 +549,11 @@ def test_upload_asset_succeeds( @pytest.mark.parametrize("status_code", (400, 500, 503)) @pytest.mark.parametrize("mock_release_id", range(3)) def test_upload_asset_fails( - default_gitea_client, example_changelog_md, status_code, mock_release_id + init_example_project: None, + default_gitea_client: Gitea, + example_changelog_md: Path, + status_code: int, + mock_release_id: int, ): urlparams = {"name": example_changelog_md.name} with requests_mock.Mocker(session=default_gitea_client.session) as m: diff --git a/tests/unit/semantic_release/hvcs/test_github.py b/tests/unit/semantic_release/hvcs/test_github.py index a17697fa2..f682505a8 100644 --- a/tests/unit/semantic_release/hvcs/test_github.py +++ b/tests/unit/semantic_release/hvcs/test_github.py @@ -1,8 +1,11 @@ +from __future__ import annotations + import base64 import glob import mimetypes import os import re +from typing import TYPE_CHECKING from unittest import mock from urllib.parse import urlencode @@ -16,6 +19,9 @@ from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, RELEASE_NOTES from tests.util import netrc_file +if TYPE_CHECKING: + from pathlib import Path + @pytest.fixture def default_gh_client(): @@ -564,7 +570,11 @@ def test_asset_upload_url(default_gh_client): @pytest.mark.parametrize("status_code", (200, 201)) @pytest.mark.parametrize("mock_release_id", range(3)) def test_upload_asset_succeeds( - default_gh_client, example_changelog_md, status_code, mock_release_id + init_example_project: None, + default_gh_client: Github, + example_changelog_md: Path, + status_code: int, + mock_release_id: int, ): label = "abc123" urlparams = {"name": example_changelog_md.name, "label": label} @@ -618,7 +628,11 @@ def test_upload_asset_succeeds( @pytest.mark.parametrize("status_code", (400, 404, 429, 500, 503)) @pytest.mark.parametrize("mock_release_id", range(3)) def test_upload_asset_fails( - default_gh_client, example_changelog_md, status_code, mock_release_id + init_example_project: None, + default_gh_client: Github, + example_changelog_md: Path, + status_code: int, + mock_release_id: int, ): label = "abc123" urlparams = {"name": example_changelog_md.name, "label": label} diff --git a/tests/unit/semantic_release/version/test_declaration.py b/tests/unit/semantic_release/version/test_declaration.py index 9d2b1e85e..ef10073e2 100644 --- a/tests/unit/semantic_release/version/test_declaration.py +++ b/tests/unit/semantic_release/version/test_declaration.py @@ -15,7 +15,8 @@ from tests.const import EXAMPLE_PROJECT_VERSION -def test_pyproject_toml_version_found(example_pyproject_toml): +@pytest.mark.usefixtures("init_example_project") +def test_pyproject_toml_version_found(example_pyproject_toml: Path): decl = TomlVersionDeclaration( example_pyproject_toml.resolve(), "tool.poetry.version" ) @@ -24,7 +25,8 @@ def test_pyproject_toml_version_found(example_pyproject_toml): assert versions.pop() == Version.parse(EXAMPLE_PROJECT_VERSION) -def test_setup_cfg_version_found(example_setup_cfg): +@pytest.mark.usefixtures("init_example_project") +def test_setup_cfg_version_found(example_setup_cfg: Path): decl = PatternVersionDeclaration( example_setup_cfg.resolve(), r"^version *= *(?P.*)$" ) @@ -48,6 +50,7 @@ def test_setup_cfg_version_found(example_setup_cfg): ), ], ) +@pytest.mark.usefixtures("init_example_project") def test_version_replace(decl_cls, config_file, search_text): new_version = Version(1, 0, 0) decl = decl_cls(config_file.resolve(), search_text) From c7be6e2330c3527302915fcf3334923ab5252480 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 6 Feb 2024 21:56:07 +0000 Subject: [PATCH 049/167] style: beautify 229c6471efc2c1bee002c3b89f58caf391b89e78 --- tests/fixtures/example_project.py | 9 ++++++--- tests/unit/semantic_release/cli/test_config.py | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 8d6c21459..6c9fb895c 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -81,9 +81,10 @@ def example_project_dir(tmp_path: Path) -> ExProjectDir: return tmp_path.resolve() - @pytest.fixture -def change_to_ex_proj_dir(example_project_dir: ExProjectDir) -> Generator[None, None, None]: +def change_to_ex_proj_dir( + example_project_dir: ExProjectDir, +) -> Generator[None, None, None]: cwd = os.getcwd() tgt_dir = str(example_project_dir.resolve()) if cwd == tgt_dir: @@ -155,7 +156,9 @@ def hello_world() -> None: abs_filepath.write_text(contents) # create the changelog template directory - cached_project_path.joinpath(changelog_template_dir).mkdir(parents=True, exist_ok=True) + cached_project_path.joinpath(changelog_template_dir).mkdir( + parents=True, exist_ok=True + ) # trigger automatic cleanup of cache directory during teardown return teardown_cached_dir(cached_project_path) diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index aac47b18c..8956c259c 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -141,7 +141,10 @@ def test_default_toml_config_valid(example_project_dir): ], ) def test_commit_author_configurable( - example_pyproject_toml: Path, repo_with_no_tags_angular_commits, mock_env, expected_author + example_pyproject_toml: Path, + repo_with_no_tags_angular_commits, + mock_env, + expected_author, ): content = tomlkit.loads(example_pyproject_toml.read_text(encoding="utf-8")).unwrap() From bf961436fd81c6398ca2c456143b64517a2b4cac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 22:02:10 +0000 Subject: [PATCH 050/167] ci: bump the github-actions group with 3 updates (#831) --- .github/workflows/main.yml | 16 ++++++++-------- .github/workflows/pr.yml | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2fa4a5784..553ba1bbb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,14 +17,14 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} @@ -43,9 +43,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 - name: Install mypy & dev packages @@ -75,12 +75,12 @@ jobs: steps: - name: Set up Python 3.9 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install Ruff run: python -m pip install ".[dev]" @@ -115,7 +115,7 @@ jobs: contents: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.ref_name }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7a0259807..72973afd2 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -13,15 +13,15 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} @@ -40,9 +40,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 - name: Install mypy & dev packages @@ -63,7 +63,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: wagoid/commitlint-github-action@v5 From 8e3c00b238859559d82ff692bcee15f70bf4f6ad Mon Sep 17 00:00:00 2001 From: Rolf Erik Lekang Date: Thu, 8 Feb 2024 09:13:57 +0100 Subject: [PATCH 051/167] ci: Configure trusted publishing in pypi --- .github/workflows/main.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 553ba1bbb..bdab128aa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -109,6 +109,9 @@ jobs: concurrency: push needs: [test, lint, beautify] if: github.repository == 'python-semantic-release/python-semantic-release' + environment: + name: pypi + url: https://pypi.org/project/python-semantic-release/ permissions: # https://docs.github.com/en/rest/overview/permissions-required-for-github-apps?apiVersion=2022-11-28#metadata id-token: write @@ -126,20 +129,15 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} root_options: "-vv" - # https://github.com/pypa/gh-action-pypi-publish#specifying-a-different-username - # This will need converting to use trusted publishing at a later date # see https://docs.pypi.org/trusted-publishers/ - name: Publish package distributions to PyPI id: pypi-publish - # NOTE: DO NOT wrap the conditional in ${{ }} as it will always evaluate to true. # See https://github.com/actions/runner/issues/1173 if: steps.release.outputs.released == 'true' uses: pypa/gh-action-pypi-publish@release/v1 with: verbose: true - user: ${{ secrets.PYPI_USERNAME }} - password: ${{ secrets.PYPI_PASSWORD }} - name: Publish package distributions to GitHub Releases id: github-release From c57b0825c632da166c6dbe5b976c9edb1aa5882b Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 30 Jan 2024 00:18:13 -0500 Subject: [PATCH 052/167] test(util): add windows line-endings possibilities for commit parsing --- tests/unit/semantic_release/commit_parser/test_util.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/unit/semantic_release/commit_parser/test_util.py b/tests/unit/semantic_release/commit_parser/test_util.py index 68bc65193..130a25581 100644 --- a/tests/unit/semantic_release/commit_parser/test_util.py +++ b/tests/unit/semantic_release/commit_parser/test_util.py @@ -7,11 +7,19 @@ "text, expected", [ ("", []), - ("\n\n \n\n \n", [" ", " "]), + ("\n\n \n\n \n", []), # Unix (LF) - empty lines + ("\r\n\r\n \r\n\r\n \n", []), # Windows (CRLF) - empty lines + ("\n\nA\n\nB\n", ["A", "B"]), # Unix (LF) + ("\r\n\r\nA\r\n\r\nB\n", ["A", "B"]), # Windows (CRLF) ( "Long\nexplanation\n\nfull of interesting\ndetails", ["Long explanation", "full of interesting details"], ), + ( + # Windows (CRLF) + "Long\r\nexplanation\r\n\r\nfull of interesting\r\ndetails", + ["Long explanation", "full of interesting details"], + ), ], ) def test_parse_paragraphs(text, expected): From 70193ba117c1a6d3690aed685fee8a734ba174e5 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 29 Jan 2024 23:54:56 -0500 Subject: [PATCH 053/167] fix(util): properly parse windows line-endings in commit messages Due to windows line-endings `\r\n`, it would improperly split the commit description (it failed to split at all) and cause detection of Breaking changes to fail. The breaking changes regular expression looks to the start of the line for the proper syntax. Resolves: #820 --- semantic_release/commit_parser/util.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/semantic_release/commit_parser/util.py b/semantic_release/commit_parser/util.py index f93179bef..90e1c769c 100644 --- a/semantic_release/commit_parser/util.py +++ b/semantic_release/commit_parser/util.py @@ -9,11 +9,14 @@ def parse_paragraphs(text: str) -> list[str]: """ This will take a text block and return a list containing each paragraph with single line breaks collapsed into spaces. + + To handle Windows line endings, carriage returns '\r' are removed before + separating into paragraphs. + :param text: The text string to be divided. :return: A list of condensed paragraphs, as strings. """ - return [ - paragraph.replace("\n", " ") - for paragraph in text.split("\n\n") - if len(paragraph) > 0 - ] + return list(filter(None, [ + paragraph.replace("\n", " ").strip() + for paragraph in text.replace("\r", "").split("\n\n") + ])) From c777bb261e8e46497c5c4b3fd84303db71772142 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 8 Feb 2024 08:29:58 +0000 Subject: [PATCH 054/167] style: beautify 70193ba117c1a6d3690aed685fee8a734ba174e5 --- semantic_release/commit_parser/util.py | 13 +++++++++---- .../semantic_release/commit_parser/test_util.py | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/semantic_release/commit_parser/util.py b/semantic_release/commit_parser/util.py index 90e1c769c..8bffae7fe 100644 --- a/semantic_release/commit_parser/util.py +++ b/semantic_release/commit_parser/util.py @@ -16,7 +16,12 @@ def parse_paragraphs(text: str) -> list[str]: :param text: The text string to be divided. :return: A list of condensed paragraphs, as strings. """ - return list(filter(None, [ - paragraph.replace("\n", " ").strip() - for paragraph in text.replace("\r", "").split("\n\n") - ])) + return list( + filter( + None, + [ + paragraph.replace("\n", " ").strip() + for paragraph in text.replace("\r", "").split("\n\n") + ], + ) + ) diff --git a/tests/unit/semantic_release/commit_parser/test_util.py b/tests/unit/semantic_release/commit_parser/test_util.py index 130a25581..ea7037b0d 100644 --- a/tests/unit/semantic_release/commit_parser/test_util.py +++ b/tests/unit/semantic_release/commit_parser/test_util.py @@ -7,9 +7,9 @@ "text, expected", [ ("", []), - ("\n\n \n\n \n", []), # Unix (LF) - empty lines - ("\r\n\r\n \r\n\r\n \n", []), # Windows (CRLF) - empty lines - ("\n\nA\n\nB\n", ["A", "B"]), # Unix (LF) + ("\n\n \n\n \n", []), # Unix (LF) - empty lines + ("\r\n\r\n \r\n\r\n \n", []), # Windows (CRLF) - empty lines + ("\n\nA\n\nB\n", ["A", "B"]), # Unix (LF) ("\r\n\r\nA\r\n\r\nB\n", ["A", "B"]), # Windows (CRLF) ( "Long\nexplanation\n\nfull of interesting\ndetails", From 460c73bdc62f67848b0c984c1cfe2ad00d21e9d0 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Thu, 8 Feb 2024 08:32:14 +0000 Subject: [PATCH 055/167] 9.0.2 Automatically generated by python-semantic-release --- CHANGELOG.md | 64 +++++++++++++++++++++++++++++------- pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8011d81d8..7cad8feb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,48 @@ +## v9.0.2 (2024-02-08) + +### Chore + +* chore: update pre-commit hooks (#796) ([`e238452`](https://github.com/python-semantic-release/python-semantic-release/commit/e23845226120b4fb934dd8755ce1b3f822cac041)) + +### Ci + +* ci: Configure trusted publishing in pypi ([`8e3c00b`](https://github.com/python-semantic-release/python-semantic-release/commit/8e3c00b238859559d82ff692bcee15f70bf4f6ad)) + +* ci: bump the github-actions group with 3 updates (#831) ([`bf96143`](https://github.com/python-semantic-release/python-semantic-release/commit/bf961436fd81c6398ca2c456143b64517a2b4cac)) + +* ci: add grouped github-actions section to dependabot config (#794) ([`3eb15c4`](https://github.com/python-semantic-release/python-semantic-release/commit/3eb15c413cec0430fe2b27a313185068f900c61d)) + +### Documentation + +* docs: Remove duplicate note in configuration.rst (#807) ([`fb6f243`](https://github.com/python-semantic-release/python-semantic-release/commit/fb6f243a141642c02469f1080180ecaf4f3cec66)) + +### Fix + +* fix(util): properly parse windows line-endings in commit messages + +Due to windows line-endings `\r\n`, it would improperly split the commit +description (it failed to split at all) and cause detection of Breaking changes +to fail. The breaking changes regular expression looks to the start of the line +for the proper syntax. + +Resolves: #820 ([`70193ba`](https://github.com/python-semantic-release/python-semantic-release/commit/70193ba117c1a6d3690aed685fee8a734ba174e5)) + +### Style + +* style: beautify 70193ba117c1a6d3690aed685fee8a734ba174e5 ([`c777bb2`](https://github.com/python-semantic-release/python-semantic-release/commit/c777bb261e8e46497c5c4b3fd84303db71772142)) + +* style: beautify 229c6471efc2c1bee002c3b89f58caf391b89e78 ([`c7be6e2`](https://github.com/python-semantic-release/python-semantic-release/commit/c7be6e2330c3527302915fcf3334923ab5252480)) + +### Test + +* test(util): add windows line-endings possibilities for commit parsing ([`c57b082`](https://github.com/python-semantic-release/python-semantic-release/commit/c57b0825c632da166c6dbe5b976c9edb1aa5882b)) + +* test(fixtures): cache the base example project directory (#799) ([`229c647`](https://github.com/python-semantic-release/python-semantic-release/commit/229c6471efc2c1bee002c3b89f58caf391b89e78)) + + ## v9.0.1 (2024-02-06) ### Fix @@ -2301,11 +2343,7 @@ Fixes #237 ([`fe6a7e7`](https://github.com/python-semantic-release/python-semant ## v7.0.0 (2020-05-22) -### Documentation - -* docs: add conda-forge badge ([`e9536bb`](https://github.com/python-semantic-release/python-semantic-release/commit/e9536bbe119c9e3b90c61130c02468e0e1f14141)) - -### Feature +### Breaking * feat(changelog): add changelog components (#240) @@ -2330,6 +2368,10 @@ BREAKING CHANGE: The `compare_url` option has been removed in favor of using Changelog components may now receive the value of `changelog_sections`, split and ready to use. ([`3e17a98`](https://github.com/python-semantic-release/python-semantic-release/commit/3e17a98d7fa8468868a87e62651ac2c010067711)) +### Documentation + +* docs: add conda-forge badge ([`e9536bb`](https://github.com/python-semantic-release/python-semantic-release/commit/e9536bbe119c9e3b90c61130c02468e0e1f14141)) + ### Style * style: improve code formatting ([`1dfca97`](https://github.com/python-semantic-release/python-semantic-release/commit/1dfca97c3856e496e9e2cda429b8aa093799bd5b)) @@ -2350,7 +2392,7 @@ Fixes #239 ([`34acbbc`](https://github.com/python-semantic-release/python-semant ## v6.4.0 (2020-05-15) -### Feature +### Breaking * feat(history): create emoji parser (#238) @@ -2719,11 +2761,7 @@ will get the version they specify. ([`123984d`](https://github.com/python-semant ## v5.0.0 (2020-03-22) -### Documentation - -* docs(pypi): update docstings in pypi.py ([`6502d44`](https://github.com/python-semantic-release/python-semantic-release/commit/6502d448fa65e5dc100e32595e83fff6f62a881a)) - -### Feature +### Breaking * feat(build): allow config setting for build command (#195) @@ -2733,6 +2771,10 @@ BREAKING CHANGE: Previously the build_commands configuration variable set the ty Closes #188 ([`740f4bd`](https://github.com/python-semantic-release/python-semantic-release/commit/740f4bdb26569362acfc80f7e862fc2c750a46dd)) +### Documentation + +* docs(pypi): update docstings in pypi.py ([`6502d44`](https://github.com/python-semantic-release/python-semantic-release/commit/6502d448fa65e5dc100e32595e83fff6f62a881a)) + ### Fix * fix: Rename default of build_command config ([`d5db22f`](https://github.com/python-semantic-release/python-semantic-release/commit/d5db22f9f7acd05d20fd60a8b4b5a35d4bbfabb8)) diff --git a/pyproject.toml b/pyproject.toml index bbe3772ed..553370906 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.0.1" +version = "9.0.2" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index cb3d7bdeb..1b6df4202 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "9.0.1" +__version__ = "9.0.2" def setup_hook(argv: list[str]) -> None: From 613d240499c081b185c5774d1d8a4665d3f5cc28 Mon Sep 17 00:00:00 2001 From: Heinz-Alexander Fuetterer Date: Sat, 6 Jan 2024 10:13:05 +0100 Subject: [PATCH 056/167] chore: modernize ruff configuration to work with ruff >= 0.2 --- pyproject.toml | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 553370906..c4ed20016 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -163,9 +163,17 @@ allow_incomplete_defs = true allow_untyped_calls = true [tool.ruff] +line-length = 88 +target-version = "py38" +force-exclude = true +output-format = "grouped" +show-fixes = true +src = ["semantic_release", "tests"] + +[tool.ruff.lint] select = ["ALL"] -# See https://beta.ruff.rs/docs/rules +# See https://docs.astral.sh/ruff/rules/ # for any of these codes you can also run `ruff rule [CODE]` # which explains it in the terminal ignore = [ @@ -257,14 +265,7 @@ ignore = [ ] external = ["V"] -target-version = "py38" -force-exclude = true -line-length = 88 -output-format = "grouped" ignore-init-module-imports = true -show-source = true -show-fixes = true -src = ["semantic_release", "tests"] task-tags = ["NOTE", "TODO", "FIXME", "XXX"] [tool.ruff.format] @@ -272,7 +273,7 @@ quote-style = "double" indent-style = "space" line-ending = "lf" -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] # Imported but unused "__init__.py" = ["F401"] # pydantic 1 can't handle __future__ annotations-enabled syntax on < 3.10 @@ -306,28 +307,28 @@ line-ending = "lf" "ANN", ] -[tool.ruff.mccabe] +[tool.ruff.lint.mccabe] max-complexity = 10 -[tool.ruff.flake8-implicit-str-concat] +[tool.ruff.lint.flake8-implicit-str-concat] allow-multiline = true -[tool.ruff.flake8-quotes] +[tool.ruff.lint.flake8-quotes] inline-quotes = "double" multiline-quotes = "double" -[tool.ruff.flake8-tidy-imports] +[tool.ruff.lint.flake8-tidy-imports] ban-relative-imports = "all" -[tool.ruff.flake8-type-checking] +[tool.ruff.lint.flake8-type-checking] strict = true -[tool.ruff.flake8-pytest-style] +[tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false mark-parentheses = false parametrize-names-type = "csv" -[tool.ruff.isort] +[tool.ruff.lint.isort] # required-imports = ["from __future__ import annotations"] combine-as-imports = true known-first-party = ["semantic_release"] From 2c8a36ea5b1d1fb19cfe90a3b8a1bce5077c717c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 3 Feb 2024 14:43:03 -0500 Subject: [PATCH 057/167] test(algorithm): add bfs unit test on fake git history --- .../version/test_algorithm.py | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/tests/unit/semantic_release/version/test_algorithm.py b/tests/unit/semantic_release/version/test_algorithm.py index 00ec35336..61debd9d1 100644 --- a/tests/unit/semantic_release/version/test_algorithm.py +++ b/tests/unit/semantic_release/version/test_algorithm.py @@ -1,12 +1,62 @@ import pytest -from git import Repo +from git import Commit, Repo, TagReference from semantic_release.enums import LevelBump -from semantic_release.version.algorithm import _increment_version, tags_and_versions +from semantic_release.version.algorithm import ( + _bfs_for_latest_version_in_history, + _increment_version, + tags_and_versions, +) from semantic_release.version.translator import VersionTranslator from semantic_release.version.version import Version +def test_bfs_for_latest_version_in_history(): + # Setup fake git graph + """ + * merge commit 6 (start) + |\ + | * commit 5 + | * commit 4 + |/ + * commit 3 + * commit 2 + * commit 1 + * v1.0.0 + """ + repo = Repo() + expected_version = Version.parse("1.0.0") + v1_commit = Commit(repo, binsha=b"0" * 20) + class TagReferenceOverride(TagReference): + commit = v1_commit # type: ignore - mocking the commit property + + v1_tag = TagReferenceOverride(repo, "refs/tags/v1.0.0", check_path=False) + + trunk = Commit(repo, binsha=b"3" * 20, parents=[ + Commit(repo, binsha=b"2" * 20, parents=[ + Commit(repo, binsha=b"1" * 20, parents=[v1_commit]), + ]), + ]) + start_commit = Commit( + repo, + binsha=b"6" * 20, + parents=[ + trunk, + Commit(repo, binsha=b"5" * 20, parents=[ + Commit(repo, binsha=b"4" * 20, parents=[trunk]), + ]), + ] + ) + + # Execute + actual = _bfs_for_latest_version_in_history(start_commit, [ + (v1_tag, expected_version), + ]) + + # Verify + assert expected_version == actual + + @pytest.mark.parametrize( "tags, sorted_tags", [ From 02df305db43abfc3a1f160a4a52cc2afae5d854f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 30 Jan 2024 00:30:05 -0500 Subject: [PATCH 058/167] fix(algorithm): correct bfs to not abort on previously visited node --- semantic_release/version/algorithm.py | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/semantic_release/version/algorithm.py b/semantic_release/version/algorithm.py index d6807ab9f..a63feff0a 100644 --- a/semantic_release/version/algorithm.py +++ b/semantic_release/version/algorithm.py @@ -71,7 +71,9 @@ def _bfs_for_latest_version_in_history( `merge_base`'s parents' history. If no commits in the history correspond to a released version, return None """ - + tag_sha_2_version_lookup = { + tag.commit.hexsha: version for tag, version in full_release_tags_and_versions + } # Step 3. Latest full release version within the history of the current branch # Breadth-first search the merge-base and its parent commits for one which matches # the tag of the latest full release tag in history @@ -81,28 +83,26 @@ def bfs(visited: set[Commit], q: Queue[Commit]) -> Version | None: return None node = q.get() - if node in visited: - log.debug("commit %s already visited, returning none", node.hexsha) - return None - for tag, version in full_release_tags_and_versions: - log.debug( - "checking if tag %r (%s) matches commit %s", - tag.name, - tag.commit.hexsha, - node.hexsha, + log.debug("checking if commit %s matches any tags", node.hexsha) + version = tag_sha_2_version_lookup.get(node.hexsha, None) + + if version is not None: + log.info( + "found latest version in branch history: %r (%s)", + str(version), + node.hexsha[:7], ) - if tag.commit == node: - log.info( - "found latest version in branch history: %r (%s)", - str(version), - node.hexsha[:7], - ) - return version - log.debug("commit %s doesn't match any tags", node.hexsha) + return version + + log.debug("commit %s doesn't match any tags", node.hexsha) visited.add(node) + for parent in node.parents: + if parent in visited: + log.debug("parent commit %s already visited", node.hexsha) + continue log.debug("queuing parent commit %s", parent.hexsha) q.put(parent) From 8b742d3db6652981a7b5f773a74b0534edc1fc15 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 3 Feb 2024 12:18:52 -0500 Subject: [PATCH 059/167] perf(algorithm): refactor bfs search to use queue rather than recursion --- semantic_release/version/algorithm.py | 72 +++++++++++++++++---------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/semantic_release/version/algorithm.py b/semantic_release/version/algorithm.py index a63feff0a..e5f51b4b8 100644 --- a/semantic_release/version/algorithm.py +++ b/semantic_release/version/algorithm.py @@ -77,42 +77,60 @@ def _bfs_for_latest_version_in_history( # Step 3. Latest full release version within the history of the current branch # Breadth-first search the merge-base and its parent commits for one which matches # the tag of the latest full release tag in history - def bfs(visited: set[Commit], q: Queue[Commit]) -> Version | None: - if q.empty(): - log.debug("queue is empty, returning none") - return None + def bfs(start_commit: Commit | TagObject | Blob | Tree) -> Version | None: + # Derived from Geeks for Geeks + # https://www.geeksforgeeks.org/python-program-for-breadth-first-search-or-bfs-for-a-graph/?ref=lbp - node = q.get() + # Create a queue for BFS + q: Queue[Commit | TagObject | Blob | Tree] = Queue() - log.debug("checking if commit %s matches any tags", node.hexsha) - version = tag_sha_2_version_lookup.get(node.hexsha, None) + # Create a set to store visited graph nodes (commit objects in this case) + visited: set[Commit | TagObject | Blob | Tree] = set() - if version is not None: - log.info( - "found latest version in branch history: %r (%s)", - str(version), - node.hexsha[:7], - ) - return version + # Add the source node in the queue & mark as visited to start the search + q.put(start_commit) + visited.add(start_commit) + + # Initialize the result to None + result = None + + # Traverse the git history until it finds a version tag if one exists + while not q.empty(): + node = q.get() + visited.add(node) + + log.debug("checking if commit %s matches any tags", node.hexsha) + version = tag_sha_2_version_lookup.get(node.hexsha, None) - log.debug("commit %s doesn't match any tags", node.hexsha) + if version is not None: + log.info( + "found latest version in branch history: %r (%s)", + str(version), + node.hexsha[:7], + ) + result = version + break - visited.add(node) + log.debug("commit %s doesn't match any tags", node.hexsha) - for parent in node.parents: - if parent in visited: - log.debug("parent commit %s already visited", node.hexsha) - continue - log.debug("queuing parent commit %s", parent.hexsha) - q.put(parent) + # Add all parent commits to the queue if they haven't been visited + for parent in node.parents: + if parent in visited: + log.debug("parent commit %s already visited", node.hexsha) + continue - return bfs(visited, q) + log.debug("queuing parent commit %s", parent.hexsha) + q.put(parent) - q: Queue[Commit] = Queue() - q.put(merge_base) - latest_version = bfs(set(), q) + return result + + # Run a Breadth First Search to find the latest version in the history + latest_version = bfs(merge_base) + if latest_version is not None: + log.info("the latest version in this branch's history is %s", latest_version) + else: + log.info("no version tags found in this branch's history") - log.info("the latest version in this branch's history is %s", latest_version) return latest_version From f95be0c207e499e214b12c578825bfcee0f2bbf8 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 8 Feb 2024 09:16:48 +0000 Subject: [PATCH 060/167] style: beautify 8b742d3db6652981a7b5f773a74b0534edc1fc15 --- semantic_release/version/algorithm.py | 1 + .../version/test_algorithm.py | 42 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/semantic_release/version/algorithm.py b/semantic_release/version/algorithm.py index e5f51b4b8..63f381bf0 100644 --- a/semantic_release/version/algorithm.py +++ b/semantic_release/version/algorithm.py @@ -74,6 +74,7 @@ def _bfs_for_latest_version_in_history( tag_sha_2_version_lookup = { tag.commit.hexsha: version for tag, version in full_release_tags_and_versions } + # Step 3. Latest full release version within the history of the current branch # Breadth-first search the merge-base and its parent commits for one which matches # the tag of the latest full release tag in history diff --git a/tests/unit/semantic_release/version/test_algorithm.py b/tests/unit/semantic_release/version/test_algorithm.py index 61debd9d1..b3b98559e 100644 --- a/tests/unit/semantic_release/version/test_algorithm.py +++ b/tests/unit/semantic_release/version/test_algorithm.py @@ -27,31 +27,47 @@ def test_bfs_for_latest_version_in_history(): repo = Repo() expected_version = Version.parse("1.0.0") v1_commit = Commit(repo, binsha=b"0" * 20) + class TagReferenceOverride(TagReference): - commit = v1_commit # type: ignore - mocking the commit property + commit = v1_commit # type: ignore - mocking the commit property v1_tag = TagReferenceOverride(repo, "refs/tags/v1.0.0", check_path=False) - trunk = Commit(repo, binsha=b"3" * 20, parents=[ - Commit(repo, binsha=b"2" * 20, parents=[ - Commit(repo, binsha=b"1" * 20, parents=[v1_commit]), - ]), - ]) + trunk = Commit( + repo, + binsha=b"3" * 20, + parents=[ + Commit( + repo, + binsha=b"2" * 20, + parents=[ + Commit(repo, binsha=b"1" * 20, parents=[v1_commit]), + ], + ), + ], + ) start_commit = Commit( repo, binsha=b"6" * 20, parents=[ trunk, - Commit(repo, binsha=b"5" * 20, parents=[ - Commit(repo, binsha=b"4" * 20, parents=[trunk]), - ]), - ] + Commit( + repo, + binsha=b"5" * 20, + parents=[ + Commit(repo, binsha=b"4" * 20, parents=[trunk]), + ], + ), + ], ) # Execute - actual = _bfs_for_latest_version_in_history(start_commit, [ - (v1_tag, expected_version), - ]) + actual = _bfs_for_latest_version_in_history( + start_commit, + [ + (v1_tag, expected_version), + ], + ) # Verify assert expected_version == actual From c90b2f54532374f0114cf381c193397df856d1ac Mon Sep 17 00:00:00 2001 From: semantic-release Date: Thu, 8 Feb 2024 09:19:13 +0000 Subject: [PATCH 061/167] 9.0.3 Automatically generated by python-semantic-release --- CHANGELOG.md | 23 +++++++++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cad8feb6..2e082be14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ +## v9.0.3 (2024-02-08) + +### Chore + +* chore: modernize ruff configuration to work with ruff >= 0.2 ([`613d240`](https://github.com/python-semantic-release/python-semantic-release/commit/613d240499c081b185c5774d1d8a4665d3f5cc28)) + +### Fix + +* fix(algorithm): correct bfs to not abort on previously visited node ([`02df305`](https://github.com/python-semantic-release/python-semantic-release/commit/02df305db43abfc3a1f160a4a52cc2afae5d854f)) + +### Performance + +* perf(algorithm): refactor bfs search to use queue rather than recursion ([`8b742d3`](https://github.com/python-semantic-release/python-semantic-release/commit/8b742d3db6652981a7b5f773a74b0534edc1fc15)) + +### Style + +* style: beautify 8b742d3db6652981a7b5f773a74b0534edc1fc15 ([`f95be0c`](https://github.com/python-semantic-release/python-semantic-release/commit/f95be0c207e499e214b12c578825bfcee0f2bbf8)) + +### Test + +* test(algorithm): add bfs unit test on fake git history ([`2c8a36e`](https://github.com/python-semantic-release/python-semantic-release/commit/2c8a36ea5b1d1fb19cfe90a3b8a1bce5077c717c)) + + ## v9.0.2 (2024-02-08) ### Chore diff --git a/pyproject.toml b/pyproject.toml index c4ed20016..c45ea3f36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.0.2" +version = "9.0.3" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 1b6df4202..b41020466 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "9.0.2" +__version__ = "9.0.3" def setup_hook(argv: list[str]) -> None: From 291aacea1d0429a3b27e92b0a20b598f43f6ea6b Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 8 Feb 2024 23:53:09 -0500 Subject: [PATCH 062/167] build(deps): bump minimum required `tomlkit` to `>=0.11.0` TOMLDocument is missing the `unwrap()` function in `v0.10.2` which causes an AttributeError to occur when attempting to read a the text in `pyproject.toml` as discovered with #834 Resolves: #834 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c45ea3f36..dd99fd184 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ "requests>=2.25,<3", "jinja2>=3.1.2,<4", "python-gitlab>=2,<5", - "tomlkit~=0.10", + "tomlkit~=0.11", "dotty-dict>=1.3.0,<2", "importlib-resources>=5.7,<7", "pydantic>=2,<3", From 0193cde64cf7e07a7ecd5a0ea9513105f6207386 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 8 Jan 2024 23:08:25 -0500 Subject: [PATCH 063/167] test(fixtures): adapt cli conftest fixtures to read when file is known to exist --- tests/command_line/conftest.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/tests/command_line/conftest.py b/tests/command_line/conftest.py index 823cf9e59..a8532a1ab 100644 --- a/tests/command_line/conftest.py +++ b/tests/command_line/conftest.py @@ -23,6 +23,7 @@ if TYPE_CHECKING: from pathlib import Path + from typing import Protocol from git.repo import Repo from pytest import MonkeyPatch @@ -30,6 +31,14 @@ from semantic_release.changelog.release_history import ReleaseHistory + from tests.fixtures.example_project import ExProjectDir + + class ReadConfigFileFn(Protocol): + """Read the raw config file from `config_path`.""" + + def __call__(self, file: Path | str) -> RawConfig: + ... + @pytest.fixture def cli_runner() -> CliRunner: @@ -53,20 +62,17 @@ def mocked_git_push(monkeypatch: MonkeyPatch) -> MagicMock: @pytest.fixture -def config_path(example_project_dir: Path) -> Path: +def config_path(example_project_dir: ExProjectDir) -> Path: return example_project_dir / DEFAULT_CONFIG_FILE @pytest.fixture -def raw_config(config_path: Path) -> RawConfig: - """ - Read the raw config file from `config_path`. +def read_config_file() -> ReadConfigFileFn: + def _read_config_file(file: Path | str) -> RawConfig: + config_text = load_raw_config_file(file) + return RawConfig.model_validate(config_text) - note: a `tests.fixtures.example_project` fixture must precede this one - otherwise, the `config_path` file will not exist, and this fixture will fail - """ - config_text = load_raw_config_file(config_path) - return RawConfig.model_validate(config_text) + return _read_config_file @pytest.fixture @@ -82,9 +88,10 @@ def cli_options(config_path: Path) -> GlobalCommandLineOptions: @pytest.fixture def runtime_context_with_tags( repo_with_single_branch_and_prereleases_angular_commits: Repo, - raw_config: RawConfig, + read_config_file: ReadConfigFileFn, cli_options: GlobalCommandLineOptions, ) -> RuntimeContext: + raw_config = read_config_file(cli_options.config_file) return RuntimeContext.from_raw_config( raw_config, repo_with_single_branch_and_prereleases_angular_commits, @@ -100,9 +107,10 @@ def release_history(runtime_context_with_tags: RuntimeContext) -> ReleaseHistory @pytest.fixture def runtime_context_with_no_tags( repo_with_no_tags_angular_commits: Repo, - raw_config: RawConfig, + read_config_file: ReadConfigFileFn, cli_options: GlobalCommandLineOptions, ) -> RuntimeContext: + raw_config = read_config_file(cli_options.config_file) return RuntimeContext.from_raw_config( raw_config, repo_with_no_tags_angular_commits, From 0cd0f44019a5dacf01040b6917b35f2a918d00ca Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 8 Jan 2024 23:34:55 -0500 Subject: [PATCH 064/167] test(fixtures): cache the base example git directory --- tests/fixtures/example_project.py | 21 +-- tests/fixtures/git_repo.py | 227 +++++++++++++++++++++--------- tests/util.py | 13 ++ 3 files changed, 173 insertions(+), 88 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 6c9fb895c..70fbe3e55 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -1,8 +1,6 @@ from __future__ import annotations import os -import shutil -import sys from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING, Generator @@ -27,6 +25,7 @@ EXAMPLE_SETUP_CFG_CONTENT, EXAMPLE_SETUP_PY_CONTENT, ) +from tests.util import copy_dir_tree if TYPE_CHECKING: from typing import Any, Protocol @@ -177,23 +176,7 @@ def init_example_project( ) # Copy the cached project files into the current test's project directory - if sys.version_info[:2] == (3, 7): - # For 3.7 compatibility, destination can't exist, and dirs_exist_ok isn't available - # since destination had to be removed, handle changing directories to prevent error - os.chdir(str(example_project_dir.parent)) - shutil.rmtree(str(example_project_dir), ignore_errors=True) - shutil.copytree( - src=str(cached_example_project), - dst=str(example_project_dir), - ) - os.chdir(str(example_project_dir)) - return - - shutil.copytree( - src=str(cached_example_project), - dst=str(example_project_dir), - dirs_exist_ok=True, - ) + copy_dir_tree(cached_example_project, example_project_dir) @pytest.fixture diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 7080fd894..f3b3f6933 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -4,14 +4,15 @@ import pytest from git import Actor, Repo -from pytest_lazyfixture import lazy_fixture from tests.const import COMMIT_MESSAGE, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER -from tests.util import add_text_to_file, shortuid +from tests.util import add_text_to_file, copy_dir_tree, shortuid if TYPE_CHECKING: + from pathlib import Path from typing import Generator, Protocol + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import ( ExProjectDir, UpdatePyprojectTomlFn, @@ -19,11 +20,11 @@ ) class RepoInitFn(Protocol): - def __call__(self) -> Repo: + def __call__(self, remote_url: str | None = None) -> Repo: ... -@pytest.fixture +@pytest.fixture(scope="session") def commit_author(): return Actor(name="semantic release testing", email="not_a_real@email.com") @@ -38,36 +39,80 @@ def example_git_ssh_url(): return f"git@example.com:{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}.git" -@pytest.fixture +@pytest.fixture(scope="session") def example_git_https_url(): return f"https://example.com/{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}" -@pytest.fixture( - # For the moment there's no value in re-running every test that wants a repo - # twice, once with a different URL format - params=[lazy_fixture("example_git_ssh_url")] -) +@pytest.fixture(scope="session") +def cached_example_git_project( + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, + cached_example_project: Path, + example_git_https_url: str, + commit_author: Actor, +) -> Path: + """ + Initializes an example project with git repo. DO NOT USE DIRECTLY. + + Use the `git_repo_factory` fixture instead. This creates a default + base repository, all settings can be changed later through the git + repo factory fixture. + """ + if not cached_example_project.exists(): + raise RuntimeError("Unable to find cached project files") + + cached_git_proj_path = (cached_files_dir / "example_git_project").resolve() + + # make a copy of the example project as a base + copy_dir_tree(cached_example_project, cached_git_proj_path) + + # initialize git repo (open and close) + # NOTE: We don't want to hold the repo object open for the entire test session, + # the implementation on Windows holds some file descriptors open until close is called. + with Repo.init(cached_git_proj_path) as repo: + # Without this the global config may set it to "master", we want consistency + repo.git.branch("-M", "main") + with repo.config_writer("repository") as config: + config.set_value("user", "name", commit_author.name) + config.set_value("user", "email", commit_author.email) + config.set_value("commit", "gpgsign", False) + + repo.create_remote(name="origin", url=example_git_https_url) + + # make sure all base files are in index to enable initial commit + repo.index.add(("*", ".gitignore")) + + # TODO: initial commit! + + # trigger automatic cleanup of cache directory during teardown + return teardown_cached_dir(cached_git_proj_path) + + +@pytest.fixture def git_repo_factory( - request: pytest.FixtureRequest, - init_example_project: None, + cached_example_git_project: Path, example_project_dir: ExProjectDir, ) -> Generator[RepoInitFn, None, None]: repos: list[Repo] = [] - def git_repo() -> Repo: - repo = Repo.init(example_project_dir.resolve()) + def git_repo(remote_url: str | None = None) -> Repo: + if not cached_example_git_project.exists(): + raise RuntimeError("Unable to find cached git project files!") + + # Copy the cached git project to the current test's project dir + copy_dir_tree(cached_example_git_project, example_project_dir) + + # Create Git Repo object for project + repo = Repo(example_project_dir) # store the repo so we can close it later repos.append(repo) - # Without this the global config may set it to "master", we want consistency - repo.git.branch("-M", "main") - with repo.config_writer("repository") as config: - config.set_value("user", "name", "semantic release testing") - config.set_value("user", "email", "not_a_real@email.com") - config.set_value("commit", "gpgsign", False) - repo.create_remote(name="origin", url=request.param) + if remote_url is not None: + # update the origin url if desired + repo.remotes.origin.set_url(remote_url) + return repo try: @@ -79,13 +124,15 @@ def git_repo() -> Repo: @pytest.fixture def repo_with_no_tags_angular_commits( - git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_angular_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -100,13 +147,15 @@ def repo_with_no_tags_angular_commits( @pytest.fixture def repo_with_no_tags_emoji_commits( - git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -121,13 +170,15 @@ def repo_with_no_tags_emoji_commits( @pytest.fixture def repo_with_no_tags_scipy_commits( - git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -142,13 +193,15 @@ def repo_with_no_tags_scipy_commits( @pytest.fixture def repo_with_no_tags_tag_commits( - git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_tag_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -163,13 +216,15 @@ def repo_with_no_tags_tag_commits( @pytest.fixture def repo_with_single_branch_angular_commits( - git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_angular_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -188,13 +243,15 @@ def repo_with_single_branch_angular_commits( @pytest.fixture def repo_with_single_branch_emoji_commits( - git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -213,13 +270,15 @@ def repo_with_single_branch_emoji_commits( @pytest.fixture def repo_with_single_branch_scipy_commits( - git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -238,13 +297,15 @@ def repo_with_single_branch_scipy_commits( @pytest.fixture def repo_with_single_branch_tag_commits( - git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_tag_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -263,13 +324,15 @@ def repo_with_single_branch_tag_commits( @pytest.fixture def repo_with_single_branch_and_prereleases_angular_commits( - git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_angular_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -303,13 +366,15 @@ def repo_with_single_branch_and_prereleases_angular_commits( @pytest.fixture def repo_with_single_branch_and_prereleases_emoji_commits( - git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -343,13 +408,15 @@ def repo_with_single_branch_and_prereleases_emoji_commits( @pytest.fixture def repo_with_single_branch_and_prereleases_scipy_commits( - git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -383,13 +450,15 @@ def repo_with_single_branch_and_prereleases_scipy_commits( @pytest.fixture def repo_with_single_branch_and_prereleases_tag_commits( - git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_tag_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -427,6 +496,7 @@ def repo_with_main_and_feature_branches_angular_commits( use_angular_parser: UseParserFn, update_pyproject_toml: UpdatePyprojectTomlFn, file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_angular_parser() @@ -436,7 +506,6 @@ def repo_with_main_and_feature_branches_angular_commits( ) add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -483,13 +552,15 @@ def repo_with_main_and_feature_branches_angular_commits( @pytest.fixture def repo_with_main_and_feature_branches_emoji_commits( - git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -536,13 +607,15 @@ def repo_with_main_and_feature_branches_emoji_commits( @pytest.fixture def repo_with_main_and_feature_branches_scipy_commits( - git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -589,13 +662,15 @@ def repo_with_main_and_feature_branches_scipy_commits( @pytest.fixture def repo_with_main_and_feature_branches_tag_commits( - git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_tag_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -646,6 +721,7 @@ def repo_with_git_flow_angular_commits( use_angular_parser: UseParserFn, update_pyproject_toml: UpdatePyprojectTomlFn, file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_angular_parser() @@ -655,7 +731,6 @@ def repo_with_git_flow_angular_commits( ) add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -727,13 +802,15 @@ def repo_with_git_flow_angular_commits( @pytest.fixture def repo_with_git_flow_emoji_commits( - git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -805,13 +882,15 @@ def repo_with_git_flow_emoji_commits( @pytest.fixture def repo_with_git_flow_scipy_commits( - git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -883,13 +962,15 @@ def repo_with_git_flow_scipy_commits( @pytest.fixture def repo_with_git_flow_tag_commits( - git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_tag_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -967,6 +1048,7 @@ def repo_with_git_flow_and_release_channels_angular_commits( use_angular_parser: UseParserFn, update_pyproject_toml: UpdatePyprojectTomlFn, file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_angular_parser() @@ -976,7 +1058,6 @@ def repo_with_git_flow_and_release_channels_angular_commits( ) add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -1054,13 +1135,15 @@ def repo_with_git_flow_and_release_channels_angular_commits( @pytest.fixture def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( - git_repo_factory: RepoInitFn, use_angular_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() - use_angular_parser() # TODO: is this correct? + use_angular_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -1138,13 +1221,15 @@ def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( @pytest.fixture def repo_with_git_flow_and_release_channels_emoji_commits( - git_repo_factory: RepoInitFn, use_emoji_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_emoji_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -1222,13 +1307,15 @@ def repo_with_git_flow_and_release_channels_emoji_commits( @pytest.fixture def repo_with_git_flow_and_release_channels_scipy_commits( - git_repo_factory: RepoInitFn, use_scipy_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_scipy_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) @@ -1306,13 +1393,15 @@ def repo_with_git_flow_and_release_channels_scipy_commits( @pytest.fixture def repo_with_git_flow_and_release_channels_tag_commits( - git_repo_factory: RepoInitFn, use_tag_parser: UseParserFn, file_in_repo: str + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, ) -> Repo: git_repo = git_repo_factory() use_tag_parser() add_text_to_file(git_repo, file_in_repo) - git_repo.index.add(("*", ".gitignore")) git_repo.git.commit(m="Initial commit") add_text_to_file(git_repo, file_in_repo) diff --git a/tests/util.py b/tests/util.py index 72db9a2b0..cc29734b0 100644 --- a/tests/util.py +++ b/tests/util.py @@ -2,8 +2,10 @@ import os import secrets +import shutil import string from contextlib import contextmanager +from pathlib import Path from tempfile import NamedTemporaryFile from typing import TYPE_CHECKING, Any, Iterable, Tuple, TypeVar @@ -17,6 +19,7 @@ if TYPE_CHECKING: import filecmp + from pathlib import Path try: from typing import TypeAlias @@ -32,6 +35,16 @@ GitCommandWrapperType: TypeAlias = main.Repo.GitCommandWrapperType +def copy_dir_tree(src_dir: Path | str, dst_dir: Path | str) -> None: + """Compatibility wrapper for shutil.copytree""" + # python3.8+ + shutil.copytree( + src=str(src_dir), + dst=str(dst_dir), + dirs_exist_ok=True, + ) + + def shortuid(length: int = 8) -> str: alphabet = string.ascii_lowercase + string.digits From 1890cf2e8989e5fea182894e663c3659edf6903a Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 9 Jan 2024 22:48:24 -0500 Subject: [PATCH 065/167] test(fixtures): deconflict colliding fixtures for file dependent fixture execution --- tests/command_line/conftest.py | 46 ++++++++-------------------- tests/command_line/test_changelog.py | 22 +++++++------ tests/command_line/test_version.py | 40 +++++++++++++++++------- tests/fixtures/example_project.py | 31 +++++++++++++++---- 4 files changed, 80 insertions(+), 59 deletions(-) diff --git a/tests/command_line/conftest.py b/tests/command_line/conftest.py index a8532a1ab..85c5efdf1 100644 --- a/tests/command_line/conftest.py +++ b/tests/command_line/conftest.py @@ -16,10 +16,7 @@ from semantic_release.cli.const import DEFAULT_CONFIG_FILE from semantic_release.cli.util import load_raw_config_file -from tests.util import ( - get_release_history_from_context, - prepare_mocked_git_command_wrapper_type, -) +from tests.util import prepare_mocked_git_command_wrapper_type if TYPE_CHECKING: from pathlib import Path @@ -29,8 +26,6 @@ from pytest import MonkeyPatch from requests_mock.mocker import Mocker - from semantic_release.changelog.release_history import ReleaseHistory - from tests.fixtures.example_project import ExProjectDir class ReadConfigFileFn(Protocol): @@ -39,6 +34,12 @@ class ReadConfigFileFn(Protocol): def __call__(self, file: Path | str) -> RawConfig: ... + class RetrieveRuntimeContextFn(Protocol): + """Retrieve the runtime context for a repo.""" + + def __call__(self, repo: Repo) -> RuntimeContext: + ... + @pytest.fixture def cli_runner() -> CliRunner: @@ -86,33 +87,12 @@ def cli_options(config_path: Path) -> GlobalCommandLineOptions: @pytest.fixture -def runtime_context_with_tags( - repo_with_single_branch_and_prereleases_angular_commits: Repo, +def retrieve_runtime_context( read_config_file: ReadConfigFileFn, cli_options: GlobalCommandLineOptions, -) -> RuntimeContext: - raw_config = read_config_file(cli_options.config_file) - return RuntimeContext.from_raw_config( - raw_config, - repo_with_single_branch_and_prereleases_angular_commits, - cli_options, - ) - +) -> RetrieveRuntimeContextFn: + def _retrieve_runtime_context(repo: Repo) -> RuntimeContext: + raw_config = read_config_file(cli_options.config_file) + return RuntimeContext.from_raw_config(raw_config, repo, cli_options) -@pytest.fixture -def release_history(runtime_context_with_tags: RuntimeContext) -> ReleaseHistory: - return get_release_history_from_context(runtime_context_with_tags) - - -@pytest.fixture -def runtime_context_with_no_tags( - repo_with_no_tags_angular_commits: Repo, - read_config_file: ReadConfigFileFn, - cli_options: GlobalCommandLineOptions, -) -> RuntimeContext: - raw_config = read_config_file(cli_options.config_file) - return RuntimeContext.from_raw_config( - raw_config, - repo_with_no_tags_angular_commits, - cli_options, - ) + return _retrieve_runtime_context diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index c94c76eca..5ac8bf14f 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -19,7 +19,7 @@ EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, ) -from tests.util import flatten_dircmp +from tests.util import flatten_dircmp, get_release_history_from_context if TYPE_CHECKING: from pathlib import Path @@ -28,10 +28,8 @@ from git import Repo from requests_mock import Mocker - from semantic_release.changelog.release_history import ReleaseHistory - from semantic_release.cli.config import RuntimeContext - - from tests.fixtures.example_project import ExProjectDir + from tests.command_line.conftest import RetrieveRuntimeContextFn + from tests.fixtures.example_project import ExProjectDir, UseReleaseNotesTemplateFn @pytest.mark.parametrize( @@ -201,15 +199,21 @@ def test_changelog_post_to_release( ) -@pytest.mark.usefixtures("example_project_with_release_notes_template") def test_custom_release_notes_template( - release_history: ReleaseHistory, - runtime_context_with_tags: RuntimeContext, + repo_with_single_branch_and_prereleases_angular_commits: Repo, + use_release_notes_template: UseReleaseNotesTemplateFn, + retrieve_runtime_context: RetrieveRuntimeContextFn, post_mocker: Mocker, cli_runner: CliRunner, ) -> None: """Verify the template `.release_notes.md.j2` from `template_dir` is used.""" + # Setup + use_release_notes_template() + runtime_context_with_tags = retrieve_runtime_context( + repo_with_single_branch_and_prereleases_angular_commits + ) # Arrange + release_history = get_release_history_from_context(runtime_context_with_tags) tag = runtime_context_with_tags.repo.tags[-1].name version = runtime_context_with_tags.version_translator.from_tag(tag) release = release_history.released[version] @@ -227,4 +231,4 @@ def test_custom_release_notes_template( + resp.stderr ) assert post_mocker.call_count == 1 - assert post_mocker.last_request.json()["body"] == expected_release_notes + assert expected_release_notes == post_mocker.last_request.json()["body"] diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index da5c49115..5a44861bb 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -29,9 +29,12 @@ from git import Repo from requests_mock import Mocker - from semantic_release.cli.config import RuntimeContext - - from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn + from tests.command_line.conftest import RetrieveRuntimeContextFn + from tests.fixtures.example_project import ( + ExProjectDir, + UpdatePyprojectTomlFn, + UseReleaseNotesTemplateFn, + ) @pytest.mark.parametrize( @@ -573,16 +576,20 @@ def test_version_exit_code_when_not_strict( assert result.exit_code == 0 -@pytest.mark.usefixtures("example_project_with_release_notes_template") def test_custom_release_notes_template( mocked_git_push: MagicMock, - runtime_context_with_no_tags: RuntimeContext, + repo_with_no_tags_angular_commits: Repo, + use_release_notes_template: UseReleaseNotesTemplateFn, + retrieve_runtime_context: RetrieveRuntimeContextFn, post_mocker: Mocker, cli_runner: CliRunner, ) -> None: """Verify the template `.release_notes.md.j2` from `template_dir` is used.""" - # Arrange - # (see fixtures) + # Setup + use_release_notes_template() + runtime_context_with_no_tags = retrieve_runtime_context( + repo_with_no_tags_angular_commits + ) # Act resp = cli_runner.invoke( @@ -611,11 +618,14 @@ def test_custom_release_notes_template( def test_version_tag_only_push( mocked_git_push: MagicMock, - runtime_context_with_no_tags: RuntimeContext, + repo_with_no_tags_angular_commits: Repo, + retrieve_runtime_context: RetrieveRuntimeContextFn, cli_runner: CliRunner, ) -> None: - # Arrange - # (see fixtures) + # Setup + runtime_context_with_no_tags = retrieve_runtime_context( + repo_with_no_tags_angular_commits + ) head_before = runtime_context_with_no_tags.repo.head.commit # Act @@ -637,12 +647,20 @@ def test_version_tag_only_push( def test_version_only_update_files_no_git_actions( mocked_git_push: MagicMock, - runtime_context_with_tags: RuntimeContext, + repo_with_single_branch_and_prereleases_angular_commits: Repo, + use_release_notes_template: UseReleaseNotesTemplateFn, + retrieve_runtime_context: RetrieveRuntimeContextFn, cli_runner: CliRunner, tmp_path_factory: pytest.TempPathFactory, example_pyproject_toml: Path, example_project_dir: ExProjectDir, ) -> None: + # Setup + use_release_notes_template() + runtime_context_with_tags = retrieve_runtime_context( + repo_with_single_branch_and_prereleases_angular_commits + ) + # Arrange expected_new_version = "0.3.0" tempdir = tmp_path_factory.mktemp("test_version") diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 70fbe3e55..16d16c188 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -49,6 +49,10 @@ class UseParserFn(Protocol): def __call__(self) -> type[CommitParser]: ... + class UseReleaseNotesTemplateFn(Protocol): + def __call__(self) -> None: + ... + @pytest.fixture(scope="session") def pyproject_toml_file() -> Path: @@ -182,12 +186,27 @@ def init_example_project( @pytest.fixture def example_project_with_release_notes_template( init_example_project: None, - example_project_dir: Path, -) -> Path: - template_dir = example_project_dir / "templates" - release_notes_j2 = template_dir / ".release_notes.md.j2" - release_notes_j2.write_text(EXAMPLE_RELEASE_NOTES_TEMPLATE) - return example_project_dir + use_release_notes_template: UseReleaseNotesTemplateFn, +) -> None: + use_release_notes_template() + + +@pytest.fixture +def use_release_notes_template( + example_project_template_dir: Path, + changelog_template_dir: Path, + update_pyproject_toml: UpdatePyprojectTomlFn, +) -> UseReleaseNotesTemplateFn: + def _use_release_notes_template() -> None: + update_pyproject_toml( + "tool.semantic_release.changelog.template_dir", + str(changelog_template_dir), + ) + example_project_template_dir.mkdir(parents=True, exist_ok=True) + release_notes_j2 = example_project_template_dir / ".release_notes.md.j2" + release_notes_j2.write_text(EXAMPLE_RELEASE_NOTES_TEMPLATE) + + return _use_release_notes_template @pytest.fixture From 18d0877f6a6db38ed127b1c07d191075dda46904 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 10 Jan 2024 00:34:53 -0500 Subject: [PATCH 066/167] test(fixtures): modularize git repo file into sub-modules --- tests/fixtures/__init__.py | 1 + tests/fixtures/git_repo.py | 1367 +---------------- tests/fixtures/repos/__init__.py | 3 + tests/fixtures/repos/git_flow/__init__.py | 2 + .../git_flow/repo_w_2_release_channels.py | 341 ++++ .../git_flow/repo_w_3_release_channels.py | 451 ++++++ tests/fixtures/repos/github_flow/__init__.py | 1 + .../github_flow/repo_w_release_channels.py | 239 +++ .../repos/trunk_based_dev/__init__.py | 3 + .../repos/trunk_based_dev/repo_w_no_tags.py | 105 ++ .../trunk_based_dev/repo_w_prereleases.py | 182 +++ .../repos/trunk_based_dev/repo_w_tags.py | 122 ++ 12 files changed, 1453 insertions(+), 1364 deletions(-) create mode 100644 tests/fixtures/repos/__init__.py create mode 100644 tests/fixtures/repos/git_flow/__init__.py create mode 100644 tests/fixtures/repos/git_flow/repo_w_2_release_channels.py create mode 100644 tests/fixtures/repos/git_flow/repo_w_3_release_channels.py create mode 100644 tests/fixtures/repos/github_flow/__init__.py create mode 100644 tests/fixtures/repos/github_flow/repo_w_release_channels.py create mode 100644 tests/fixtures/repos/trunk_based_dev/__init__.py create mode 100644 tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py create mode 100644 tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py create mode 100644 tests/fixtures/repos/trunk_based_dev/repo_w_tags.py diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index 43c7abd7d..d9e987f57 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -1,4 +1,5 @@ from tests.fixtures.commit_parsers import * from tests.fixtures.example_project import * from tests.fixtures.git_repo import * +from tests.fixtures.repos import * from tests.fixtures.scipy import * diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index f3b3f6933..b000715f7 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -5,19 +5,15 @@ import pytest from git import Actor, Repo -from tests.const import COMMIT_MESSAGE, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER -from tests.util import add_text_to_file, copy_dir_tree, shortuid +from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER +from tests.util import copy_dir_tree, shortuid if TYPE_CHECKING: from pathlib import Path from typing import Generator, Protocol from tests.conftest import TeardownCachedDirFn - from tests.fixtures.example_project import ( - ExProjectDir, - UpdatePyprojectTomlFn, - UseParserFn, - ) + from tests.fixtures.example_project import ExProjectDir class RepoInitFn(Protocol): def __call__(self, remote_url: str | None = None) -> Repo: @@ -120,1360 +116,3 @@ def git_repo(remote_url: str | None = None) -> Repo: finally: for repo in repos: repo.close() - - -@pytest.fixture -def repo_with_no_tags_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add much more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: more text") - - return git_repo - - -@pytest.fixture -def repo_with_no_tags_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add much more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: more text") - - return git_repo - - -@pytest.fixture -def repo_with_no_tags_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add much more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: more text") - - return git_repo - - -@pytest.fixture -def repo_with_no_tags_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add much more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: more text") - - return git_repo - - -@pytest.fixture -def repo_with_single_branch_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) - git_repo.git.tag("v0.1.1", m="v0.1.1") - - assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - return git_repo - - -@pytest.fixture -def repo_with_single_branch_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) - git_repo.git.tag("v0.1.1", m="v0.1.1") - - assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - return git_repo - - -@pytest.fixture -def repo_with_single_branch_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) - git_repo.git.tag("v0.1.1", m="v0.1.1") - - assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - return git_repo - - -@pytest.fixture -def repo_with_single_branch_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) - git_repo.git.tag("v0.1.1", m="v0.1.1") - - assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - return git_repo - - -@pytest.fixture -def repo_with_single_branch_and_prereleases_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - return git_repo - - -@pytest.fixture -def repo_with_single_branch_and_prereleases_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - return git_repo - - -@pytest.fixture -def repo_with_single_branch_and_prereleases_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - return git_repo - - -@pytest.fixture -def repo_with_single_branch_and_prereleases_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - return git_repo - - -@pytest.fixture -def repo_with_main_and_feature_branches_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - update_pyproject_toml: UpdatePyprojectTomlFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - update_pyproject_toml( - "tool.semantic_release.branches.beta-testing", - {"match": "beta.*", "prerelease": True, "prerelease_token": "beta"}, - ) - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("beta_testing") - git_repo.heads.beta_testing.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) - git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") - - assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "beta_testing" - return git_repo - - -@pytest.fixture -def repo_with_main_and_feature_branches_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("beta_testing") - git_repo.heads.beta_testing.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) - git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") - - assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "beta_testing" - return git_repo - - -@pytest.fixture -def repo_with_main_and_feature_branches_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("beta_testing") - git_repo.heads.beta_testing.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) - git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") - - assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "beta_testing" - return git_repo - - -@pytest.fixture -def repo_with_main_and_feature_branches_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("beta_testing") - git_repo.heads.beta_testing.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) - git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") - - assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "beta_testing" - return git_repo - - -@pytest.fixture -def repo_with_git_flow_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - update_pyproject_toml: UpdatePyprojectTomlFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - update_pyproject_toml( - "tool.semantic_release.branches.features", - {"match": "feat.*", "prerelease": True, "prerelease_token": "alpha"}, - ) - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat!: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) - git_repo.git.tag("v1.1.0", m="v1.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) - git_repo.git.tag("v1.1.1", m="v1.1.1") - - assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) - git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (feature) add some missing text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) - git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") - - assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo - - -@pytest.fixture -def repo_with_git_flow_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":boom: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) - git_repo.git.tag("v1.1.0", m="v1.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) - git_repo.git.tag("v1.1.1", m="v1.1.1") - - assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) - git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: (feature) add some missing text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) - git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") - - assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo - - -@pytest.fixture -def repo_with_git_flow_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="API: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) - git_repo.git.tag("v1.1.0", m="v1.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) - git_repo.git.tag("v1.1.1", m="v1.1.1") - - assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) - git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: (feature) add some missing text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) - git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") - - assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo - - -@pytest.fixture -def repo_with_git_flow_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit( - m=":sparkles: add some more text\n\nBREAKING CHANGE: add some more text" - ) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) - git_repo.git.tag("v1.1.0", m="v1.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) - git_repo.git.tag("v1.1.1", m="v1.1.1") - - assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) - git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: (feature) add some missing text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) - git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") - - assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo - - -@pytest.fixture -def repo_with_git_flow_and_release_channels_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - update_pyproject_toml: UpdatePyprojectTomlFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - update_pyproject_toml( - "tool.semantic_release.branches.features", - {"match": "feat.*", "prerelease": True, "prerelease_token": "alpha"}, - ) - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat!: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") - - assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") - - assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo - - -@pytest.fixture -def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("vpy0.1.0", m="vpy0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("vpy0.1.1-rc.1", m="vpy0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat!: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("vpy1.0.0-rc.1", m="vpy1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("vpy1.0.0", m="vpy1.0.0") - - assert git_repo.commit("vpy1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("vpy1.1.0-rc.1", m="vpy1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("vpy1.1.0-rc.2", m="vpy1.1.0-rc.2") - - assert git_repo.commit("vpy1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("vpy1.1.0-alpha.1", m="vpy1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("vpy1.1.0-alpha.2", m="vpy1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("vpy1.1.0-alpha.3", m="vpy1.1.0-alpha.3") - - assert git_repo.commit("vpy1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo - - -@pytest.fixture -def repo_with_git_flow_and_release_channels_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":boom: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") - - assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") - - assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo - - -@pytest.fixture -def repo_with_git_flow_and_release_channels_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="API: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") - - assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") - - assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo - - -@pytest.fixture -def repo_with_git_flow_and_release_channels_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit( - m=":sparkles: add some more text\n\nBREAKING CHANGE: add some more text" - ) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") - - assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") - - assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo diff --git a/tests/fixtures/repos/__init__.py b/tests/fixtures/repos/__init__.py new file mode 100644 index 000000000..969410237 --- /dev/null +++ b/tests/fixtures/repos/__init__.py @@ -0,0 +1,3 @@ +from tests.fixtures.repos.git_flow import * +from tests.fixtures.repos.github_flow import * +from tests.fixtures.repos.trunk_based_dev import * diff --git a/tests/fixtures/repos/git_flow/__init__.py b/tests/fixtures/repos/git_flow/__init__.py new file mode 100644 index 000000000..7b3e5c78d --- /dev/null +++ b/tests/fixtures/repos/git_flow/__init__.py @@ -0,0 +1,2 @@ +from tests.fixtures.repos.git_flow.repo_w_2_release_channels import * +from tests.fixtures.repos.git_flow.repo_w_3_release_channels import * diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py new file mode 100644 index 000000000..93de4e310 --- /dev/null +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -0,0 +1,341 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from tests.const import COMMIT_MESSAGE +from tests.util import add_text_to_file + +if TYPE_CHECKING: + from git import Repo + + from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn + from tests.fixtures.git_repo import RepoInitFn + + +@pytest.fixture +def repo_with_git_flow_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_angular_parser() + update_pyproject_toml( + "tool.semantic_release.branches.features", + {"match": "feat.*", "prerelease": True, "prerelease_token": "alpha"}, + ) + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat!: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) + git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) + git_repo.git.tag("v1.0.0", m="v1.0.0") + + assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) + git_repo.git.tag("v1.1.0", m="v1.1.0") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) + git_repo.git.tag("v1.1.1", m="v1.1.1") + + assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) + git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: (feature) add some missing text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) + git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") + + assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "feature" + return git_repo + + +@pytest.fixture +def repo_with_git_flow_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_emoji_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":boom: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) + git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) + git_repo.git.tag("v1.0.0", m="v1.0.0") + + assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) + git_repo.git.tag("v1.1.0", m="v1.1.0") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) + git_repo.git.tag("v1.1.1", m="v1.1.1") + + assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) + git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: (feature) add some missing text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) + git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") + + assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "feature" + return git_repo + + +@pytest.fixture +def repo_with_git_flow_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_scipy_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="API: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) + git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) + git_repo.git.tag("v1.0.0", m="v1.0.0") + + assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) + git_repo.git.tag("v1.1.0", m="v1.1.0") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) + git_repo.git.tag("v1.1.1", m="v1.1.1") + + assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) + git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: (feature) add some missing text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) + git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") + + assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "feature" + return git_repo + + +@pytest.fixture +def repo_with_git_flow_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_tag_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit( + m=":sparkles: add some more text\n\nBREAKING CHANGE: add some more text" + ) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) + git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) + git_repo.git.tag("v1.0.0", m="v1.0.0") + + assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) + git_repo.git.tag("v1.1.0", m="v1.1.0") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) + git_repo.git.tag("v1.1.1", m="v1.1.1") + + assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) + git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: (feature) add some missing text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) + git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") + + assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "feature" + return git_repo diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py new file mode 100644 index 000000000..23de6346f --- /dev/null +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -0,0 +1,451 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from tests.const import COMMIT_MESSAGE +from tests.util import add_text_to_file + +if TYPE_CHECKING: + from git import Repo + + from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn + from tests.fixtures.git_repo import RepoInitFn + + +@pytest.fixture +def repo_with_git_flow_and_release_channels_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_angular_parser() + update_pyproject_toml( + "tool.semantic_release.branches.features", + {"match": "feat.*", "prerelease": True, "prerelease_token": "alpha"}, + ) + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat!: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) + git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) + git_repo.git.tag("v1.0.0", m="v1.0.0") + + assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "dev" has prerelease suffix of "rc" + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) + git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) + git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") + + assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "feature" has prerelease suffix of "alpha" + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) + git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) + git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) + git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") + + assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "feature" + return git_repo + + +@pytest.fixture +def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_angular_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("vpy0.1.0", m="vpy0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("vpy0.1.1-rc.1", m="vpy0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat!: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) + git_repo.git.tag("vpy1.0.0-rc.1", m="vpy1.0.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) + git_repo.git.tag("vpy1.0.0", m="vpy1.0.0") + + assert git_repo.commit("vpy1.0.0").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "dev" has prerelease suffix of "rc" + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) + git_repo.git.tag("vpy1.1.0-rc.1", m="vpy1.1.0-rc.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) + git_repo.git.tag("vpy1.1.0-rc.2", m="vpy1.1.0-rc.2") + + assert git_repo.commit("vpy1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "feature" has prerelease suffix of "alpha" + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) + git_repo.git.tag("vpy1.1.0-alpha.1", m="vpy1.1.0-alpha.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) + git_repo.git.tag("vpy1.1.0-alpha.2", m="vpy1.1.0-alpha.2") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) + git_repo.git.tag("vpy1.1.0-alpha.3", m="vpy1.1.0-alpha.3") + + assert git_repo.commit("vpy1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "feature" + return git_repo + + +@pytest.fixture +def repo_with_git_flow_and_release_channels_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_emoji_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":boom: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) + git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) + git_repo.git.tag("v1.0.0", m="v1.0.0") + + assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "dev" has prerelease suffix of "rc" + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) + git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) + git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") + + assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "feature" has prerelease suffix of "alpha" + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) + git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) + git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) + git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") + + assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "feature" + return git_repo + + +@pytest.fixture +def repo_with_git_flow_and_release_channels_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_scipy_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="API: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) + git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) + git_repo.git.tag("v1.0.0", m="v1.0.0") + + assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "dev" has prerelease suffix of "rc" + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) + git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) + git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") + + assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "feature" has prerelease suffix of "alpha" + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) + git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) + git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) + git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") + + assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "feature" + return git_repo + + +@pytest.fixture +def repo_with_git_flow_and_release_channels_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_tag_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit( + m=":sparkles: add some more text\n\nBREAKING CHANGE: add some more text" + ) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) + git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) + git_repo.git.tag("v1.0.0", m="v1.0.0") + + assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "dev" has prerelease suffix of "rc" + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) + git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: (dev) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) + git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") + + assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha + + # Suppose branch "feature" has prerelease suffix of "alpha" + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) + git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) + git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) + git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") + + assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "feature" + return git_repo diff --git a/tests/fixtures/repos/github_flow/__init__.py b/tests/fixtures/repos/github_flow/__init__.py new file mode 100644 index 000000000..de2cbc336 --- /dev/null +++ b/tests/fixtures/repos/github_flow/__init__.py @@ -0,0 +1 @@ +from tests.fixtures.repos.github_flow.repo_w_release_channels import * diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py new file mode 100644 index 000000000..2eb66de0e --- /dev/null +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -0,0 +1,239 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from tests.const import COMMIT_MESSAGE +from tests.util import add_text_to_file + +if TYPE_CHECKING: + from git import Repo + + from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn + from tests.fixtures.git_repo import RepoInitFn + + +@pytest.fixture +def repo_with_main_and_feature_branches_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_angular_parser() + update_pyproject_toml( + "tool.semantic_release.branches.beta-testing", + {"match": "beta.*", "prerelease": True, "prerelease_token": "beta"}, + ) + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) + git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) + git_repo.git.tag("v0.2.0", m="v0.2.0") + + assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("beta_testing") + git_repo.heads.beta_testing.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) + git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") + + assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "beta_testing" + return git_repo + + +@pytest.fixture +def repo_with_main_and_feature_branches_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_emoji_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) + git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) + git_repo.git.tag("v0.2.0", m="v0.2.0") + + assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("beta_testing") + git_repo.heads.beta_testing.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) + git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") + + assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "beta_testing" + return git_repo + + +@pytest.fixture +def repo_with_main_and_feature_branches_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_scipy_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) + git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) + git_repo.git.tag("v0.2.0", m="v0.2.0") + + assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("beta_testing") + git_repo.heads.beta_testing.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) + git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") + + assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "beta_testing" + return git_repo + + +@pytest.fixture +def repo_with_main_and_feature_branches_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_tag_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) + git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) + git_repo.git.tag("v0.2.0", m="v0.2.0") + + assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha + + git_repo.create_head("beta_testing") + git_repo.heads.beta_testing.checkout() + + # Do a prerelease on the branch + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: (feature) add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) + git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") + + assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha + assert git_repo.active_branch.name == "beta_testing" + return git_repo diff --git a/tests/fixtures/repos/trunk_based_dev/__init__.py b/tests/fixtures/repos/trunk_based_dev/__init__.py new file mode 100644 index 000000000..9e40a0157 --- /dev/null +++ b/tests/fixtures/repos/trunk_based_dev/__init__.py @@ -0,0 +1,3 @@ +from tests.fixtures.repos.trunk_based_dev.repo_w_no_tags import * +from tests.fixtures.repos.trunk_based_dev.repo_w_prereleases import * +from tests.fixtures.repos.trunk_based_dev.repo_w_tags import * diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py new file mode 100644 index 000000000..a96c03234 --- /dev/null +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from tests.util import add_text_to_file + +if TYPE_CHECKING: + from git import Repo + + from tests.fixtures.example_project import UseParserFn + from tests.fixtures.git_repo import RepoInitFn + + +@pytest.fixture +def repo_with_no_tags_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_angular_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: add much more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: more text") + + return git_repo + + +@pytest.fixture +def repo_with_no_tags_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_emoji_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add much more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: more text") + + return git_repo + + +@pytest.fixture +def repo_with_no_tags_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_scipy_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: add much more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: more text") + + return git_repo + + +@pytest.fixture +def repo_with_no_tags_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_tag_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add much more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: more text") + + return git_repo diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py new file mode 100644 index 000000000..f90f82ce7 --- /dev/null +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -0,0 +1,182 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from tests.const import COMMIT_MESSAGE +from tests.util import add_text_to_file + +if TYPE_CHECKING: + from git import Repo + + from tests.fixtures.example_project import UseParserFn + from tests.fixtures.git_repo import RepoInitFn + + +@pytest.fixture +def repo_with_single_branch_and_prereleases_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_angular_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) + git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="feat: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) + git_repo.git.tag("v0.2.0", m="v0.2.0") + + assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha + return git_repo + + +@pytest.fixture +def repo_with_single_branch_and_prereleases_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_emoji_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) + git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) + git_repo.git.tag("v0.2.0", m="v0.2.0") + + assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha + return git_repo + + +@pytest.fixture +def repo_with_single_branch_and_prereleases_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_scipy_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) + git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="ENH: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) + git_repo.git.tag("v0.2.0", m="v0.2.0") + + assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha + return git_repo + + +@pytest.fixture +def repo_with_single_branch_and_prereleases_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_tag_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) + git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") + + # Do a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) + git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") + + # Do a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":sparkles: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) + git_repo.git.tag("v0.2.0", m="v0.2.0") + + assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha + return git_repo diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py new file mode 100644 index 000000000..83597265f --- /dev/null +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from tests.const import COMMIT_MESSAGE +from tests.util import add_text_to_file + +if TYPE_CHECKING: + from git import Repo + + from tests.fixtures.example_project import UseParserFn + from tests.fixtures.git_repo import RepoInitFn + + +@pytest.fixture +def repo_with_single_branch_angular_commits( + git_repo_factory: RepoInitFn, + use_angular_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_angular_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="fix: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) + git_repo.git.tag("v0.1.1", m="v0.1.1") + + assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha + return git_repo + + +@pytest.fixture +def repo_with_single_branch_emoji_commits( + git_repo_factory: RepoInitFn, + use_emoji_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_emoji_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":bug: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) + git_repo.git.tag("v0.1.1", m="v0.1.1") + + assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha + return git_repo + + +@pytest.fixture +def repo_with_single_branch_scipy_commits( + git_repo_factory: RepoInitFn, + use_scipy_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_scipy_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="MAINT: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) + git_repo.git.tag("v0.1.1", m="v0.1.1") + + assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha + return git_repo + + +@pytest.fixture +def repo_with_single_branch_tag_commits( + git_repo_factory: RepoInitFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + change_to_ex_proj_dir: None, +) -> Repo: + git_repo = git_repo_factory() + use_tag_parser() + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m="Initial commit") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) + git_repo.git.tag("v0.1.0", m="v0.1.0") + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=":nut_and_bolt: add some more text") + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) + git_repo.git.tag("v0.1.1", m="v0.1.1") + + assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha + return git_repo From 68e12f369deced9df9e2c8d08dae7a4e4ece3fee Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 10 Jan 2024 23:20:41 -0500 Subject: [PATCH 067/167] test(utils): add a utility to temporary change directory --- tests/util.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/util.py b/tests/util.py index cc29734b0..aa08582fc 100644 --- a/tests/util.py +++ b/tests/util.py @@ -7,7 +7,7 @@ from contextlib import contextmanager from pathlib import Path from tempfile import NamedTemporaryFile -from typing import TYPE_CHECKING, Any, Iterable, Tuple, TypeVar +from typing import TYPE_CHECKING, Tuple from pydantic.dataclasses import dataclass @@ -19,7 +19,7 @@ if TYPE_CHECKING: import filecmp - from pathlib import Path + from typing import Any, Generator, Iterable, TypeVar try: from typing import TypeAlias @@ -32,6 +32,8 @@ from semantic_release.cli.config import RuntimeContext + _R = TypeVar("_R") + GitCommandWrapperType: TypeAlias = main.Repo.GitCommandWrapperType @@ -45,6 +47,16 @@ def copy_dir_tree(src_dir: Path | str, dst_dir: Path | str) -> None: ) +@contextmanager +def temporary_working_directory(directory: Path | str) -> Generator[None, None, None]: + cwd = os.getcwd() + os.chdir(str(directory)) + try: + yield + finally: + os.chdir(cwd) + + def shortuid(length: int = 8) -> str: alphabet = string.ascii_lowercase + string.digits @@ -82,9 +94,6 @@ def flatten_dircmp(dcmp: filecmp.dircmp) -> list[str]: ] -_R = TypeVar("_R") - - def xdist_sort_hack(it: Iterable[_R]) -> Iterable[_R]: """ hack for pytest-xdist From a42b032ff44bedee81d20765163758c8a2ca6153 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 10 Jan 2024 23:25:00 -0500 Subject: [PATCH 068/167] test(fixtures): refactor for session level fixture use --- tests/fixtures/example_project.py | 47 ++++++++++++++++---------- tests/fixtures/git_repo.py | 55 +++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 21 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 16d16c188..dfb374534 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -210,37 +210,48 @@ def _use_release_notes_template() -> None: @pytest.fixture -def example_pyproject_toml(example_project_dir: ExProjectDir) -> Path: - return example_project_dir / "pyproject.toml" +def example_pyproject_toml( + example_project_dir: ExProjectDir, pyproject_toml_file: Path, +) -> Path: + return example_project_dir / pyproject_toml_file @pytest.fixture -def example_setup_cfg(example_project_dir: ExProjectDir) -> Path: - return example_project_dir / "setup.cfg" +def example_setup_cfg( + example_project_dir: ExProjectDir, setup_cfg_file: Path, +) -> Path: + return example_project_dir / setup_cfg_file @pytest.fixture -def example_setup_py(example_project_dir: ExProjectDir) -> Path: - return example_project_dir / "setup.py" +def example_setup_py( + example_project_dir: ExProjectDir, setup_py_file: Path, +) -> Path: + return example_project_dir / setup_py_file # Note this is just the path and the content may change @pytest.fixture -def example_changelog_md(example_project_dir: ExProjectDir) -> Path: - return example_project_dir / "CHANGELOG.md" +def example_changelog_md( + example_project_dir: ExProjectDir, changelog_md_file: Path, +) -> Path: + return example_project_dir / changelog_md_file @pytest.fixture -def example_project_template_dir(example_project_dir: ExProjectDir) -> Path: - return example_project_dir / "templates" +def example_project_template_dir( + example_project_dir: ExProjectDir, changelog_template_dir: Path, +) -> Path: + return example_project_dir / changelog_template_dir -@pytest.fixture -def update_pyproject_toml(example_pyproject_toml: Path) -> UpdatePyprojectTomlFn: +@pytest.fixture(scope="session") +def update_pyproject_toml(pyproject_toml_file: Path) -> UpdatePyprojectTomlFn: """Update the pyproject.toml file with the given content.""" def _update_pyproject_toml(setting: str, value: Any) -> None: - with open(example_pyproject_toml) as rfd: + cwd_pyproject_toml = pyproject_toml_file.resolve() + with open(cwd_pyproject_toml) as rfd: pyproject_toml = tomlkit.load(rfd) new_setting = {} @@ -255,13 +266,13 @@ def _update_pyproject_toml(setting: str, value: Any) -> None: pointer = pointer.get(part, {}) pointer.update(new_setting) - with open(example_pyproject_toml, "w") as wfd: + with open(cwd_pyproject_toml, "w") as wfd: tomlkit.dump(pyproject_toml, wfd) return _update_pyproject_toml -@pytest.fixture +@pytest.fixture(scope="session") def use_angular_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: """Modify the configuration file to use the Angular parser.""" @@ -272,7 +283,7 @@ def _use_angular_parser() -> type[CommitParser]: return _use_angular_parser -@pytest.fixture +@pytest.fixture(scope="session") def use_emoji_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: """Modify the configuration file to use the Emoji parser.""" @@ -283,7 +294,7 @@ def _use_emoji_parser() -> type[CommitParser]: return _use_emoji_parser -@pytest.fixture +@pytest.fixture(scope="session") def use_scipy_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: """Modify the configuration file to use the Scipy parser.""" @@ -294,7 +305,7 @@ def _use_scipy_parser() -> type[CommitParser]: return _use_scipy_parser -@pytest.fixture +@pytest.fixture(scope="session") def use_tag_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: """Modify the configuration file to use the Tag parser.""" diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index b000715f7..7ddade570 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -10,27 +10,57 @@ if TYPE_CHECKING: from pathlib import Path - from typing import Generator, Protocol + from typing import Generator, Literal, Mapping, Protocol from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import ExProjectDir + CommitConvention = Literal["angular", "emoji", "scipy", "tag"] + VersionStr = str + CommitMsg = str + class RepoInitFn(Protocol): def __call__(self, remote_url: str | None = None) -> Repo: ... + class ExProjectGitRepoFn(Protocol): + def __call__(self) -> Repo: + ... + + class GetVersionStringsFn(Protocol): + def __call__(self) -> list[VersionStr]: + ... + + class GetRepoDefinitionFn(Protocol): + def __call__(self, commit_type: CommitConvention = "angular") -> Mapping[VersionStr, list[CommitMsg]]: + ... + + class BuildRepoFn(Protocol): + def __call__( + self, + git_repo_path: Path | str, + commit_type: CommitConvention, + tag_format_str: str | None = None, + ) -> None: + ... + @pytest.fixture(scope="session") def commit_author(): return Actor(name="semantic release testing", email="not_a_real@email.com") -@pytest.fixture +@pytest.fixture(scope="session") +def default_tag_format_str() -> str: + return "v{version}" + + +@pytest.fixture(scope="session") def file_in_repo(): return f"file-{shortuid()}.txt" -@pytest.fixture +@pytest.fixture(scope="session") def example_git_ssh_url(): return f"git@example.com:{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}.git" @@ -116,3 +146,22 @@ def git_repo(remote_url: str | None = None) -> Repo: finally: for repo in repos: repo.close() + + +@pytest.fixture +def example_project_git_repo(example_project_dir: ExProjectDir) -> Generator[ExProjectGitRepoFn, None, None]: + repos: list[Repo] = [] + # Must be a callable function to ensure files exist before repo is opened + def _example_project_git_repo() -> Repo: + if not example_project_dir.exists(): + raise RuntimeError("Unable to find example git project!") + + repo = Repo(example_project_dir) + repos.append(repo) + return repo + + try: + yield _example_project_git_repo + finally: + for repo in repos: + repo.close() From 4e3b6b6ef3f493ca613e25b5a5bdb5545fa12e9f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 10 Jan 2024 23:25:45 -0500 Subject: [PATCH 069/167] test(fixtures): add caching to trunk development w/ tags repos --- .../repos/trunk_based_dev/repo_w_no_tags.py | 281 +++++++++--- .../trunk_based_dev/repo_w_prereleases.py | 414 +++++++++++------- .../repos/trunk_based_dev/repo_w_tags.py | 290 ++++++++---- 3 files changed, 678 insertions(+), 307 deletions(-) diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index a96c03234..0e41ebe52 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -3,103 +3,238 @@ from typing import TYPE_CHECKING import pytest +from git import Repo -from tests.util import add_text_to_file +from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory if TYPE_CHECKING: - from git import Repo - - from tests.fixtures.example_project import UseParserFn - from tests.fixtures.git_repo import RepoInitFn + from pathlib import Path + from typing import Mapping + + from tests.conftest import TeardownCachedDirFn + from tests.fixtures.example_project import ExProjectDir, UseParserFn + from tests.fixtures.git_repo import ( + BuildRepoFn, + CommitConvention, + CommitMsg, + ExProjectGitRepoFn, + GetRepoDefinitionFn, + GetVersionStringsFn, + VersionStr, + ) + + +@pytest.fixture(scope="session") +def get_commits_for_trunk_only_repo_w_no_tags() -> GetRepoDefinitionFn: + base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { + "0.1.0": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + }, + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + }, + { + "angular": "feat: add much more text", + "emoji": ":sparkles: add much more text", + "scipy": "ENH: add much more text", + "tag": ":sparkles: add much more text", + }, + { + "angular": "fix: more text", + "emoji": ":bug: more text", + "scipy": "MAINT: more text", + "tag": ":nut_and_bolt: more text", + } + ], + } + + def _get_commits_for_trunk_only_repo_w_no_tags( + commit_type: CommitConvention = "angular", + ) -> Mapping[VersionStr, list[CommitMsg]]: + definition: Mapping[VersionStr, list[CommitMsg]] = {} + for version, commits in base_definition.items(): + definition[version] = [ + message_dict[commit_type] for message_dict in commits + ] + return definition + + return _get_commits_for_trunk_only_repo_w_no_tags + + +@pytest.fixture(scope="session") +def get_versions_for_trunk_only_repo_w_no_tags( + get_commits_for_trunk_only_repo_w_no_tags: GetRepoDefinitionFn, +) -> GetVersionStringsFn: + def _get_versions_for_trunk_only_repo_w_no_tags() -> list[VersionStr]: + return list( + get_commits_for_trunk_only_repo_w_no_tags().keys() + ) + + return _get_versions_for_trunk_only_repo_w_no_tags + + +@pytest.fixture(scope="session") +def build_trunk_only_repo_w_no_tags( + get_commits_for_trunk_only_repo_w_no_tags: GetRepoDefinitionFn, + cached_example_git_project: Path, + use_angular_parser: UseParserFn, + use_emoji_parser: UseParserFn, + use_scipy_parser: UseParserFn, + use_tag_parser: UseParserFn, + file_in_repo: str, +) -> BuildRepoFn: + def _build_trunk_only_repo_w_no_tags( + git_repo_path: Path | str, + commit_type: CommitConvention, + tag_format_str: str | None = None, + ) -> None: + repo_definition = get_commits_for_trunk_only_repo_w_no_tags(commit_type) + versions = list(repo_definition.keys()) + next_version = versions[0] + + if not cached_example_git_project.exists(): + raise RuntimeError("Unable to find example git project!") + + copy_dir_tree(cached_example_git_project, git_repo_path) + + with temporary_working_directory(git_repo_path), Repo(".") as git_repo: + if commit_type == "angular": + use_angular_parser() + elif commit_type == "emoji": + use_emoji_parser() + elif commit_type == "scipy": + use_scipy_parser() + elif commit_type == "tag": + use_tag_parser() + else: + raise ValueError(f"Unknown commit type: {commit_type}") + + git_repo.git.commit(a=True, m=repo_definition[next_version][0]) # Initial commit + + for commit_msg in repo_definition[next_version][1:]: + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(a=True, m=commit_msg) + + return _build_trunk_only_repo_w_no_tags + + + +# --------------------------------------------------------------------------- # +# Session-level fixtures to use to set up cached repositories on first use # +# --------------------------------------------------------------------------- # + + +@pytest.fixture(scope="session") +def cached_repo_with_no_tags_angular_commits( + build_trunk_only_repo_w_no_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_no_tags_angular_commits.__name__ + ) + build_trunk_only_repo_w_no_tags(cached_repo_path, "angular") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_with_no_tags_emoji_commits( + build_trunk_only_repo_w_no_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_no_tags_emoji_commits.__name__ + ) + build_trunk_only_repo_w_no_tags(cached_repo_path, "emoji") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_with_no_tags_scipy_commits( + build_trunk_only_repo_w_no_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_no_tags_scipy_commits.__name__ + ) + build_trunk_only_repo_w_no_tags(cached_repo_path, "scipy") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_with_no_tags_tag_commits( + build_trunk_only_repo_w_no_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_no_tags_tag_commits.__name__ + ) + build_trunk_only_repo_w_no_tags(cached_repo_path, "tag") + return teardown_cached_dir(cached_repo_path) + + +# --------------------------------------------------------------------------- # +# Test-level fixtures to use to set up temporary test directory # +# --------------------------------------------------------------------------- # @pytest.fixture def repo_with_no_tags_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_no_tags_angular_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add much more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: more text") - - return git_repo + if not cached_repo_with_no_tags_angular_commits.exists(): + raise RuntimeError("Unable to find cached repo!") + copy_dir_tree(cached_repo_with_no_tags_angular_commits, example_project_dir) + return example_project_git_repo() @pytest.fixture def repo_with_no_tags_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_no_tags_emoji_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add much more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: more text") - - return git_repo + if not cached_repo_with_no_tags_emoji_commits.exists(): + raise RuntimeError("Unable to find cached repo!") + copy_dir_tree(cached_repo_with_no_tags_emoji_commits, example_project_dir) + return example_project_git_repo() @pytest.fixture def repo_with_no_tags_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_no_tags_scipy_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add much more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: more text") - - return git_repo + if not cached_repo_with_no_tags_scipy_commits.exists(): + raise RuntimeError("Unable to find cached repo!") + copy_dir_tree(cached_repo_with_no_tags_scipy_commits, example_project_dir) + return example_project_git_repo() @pytest.fixture def repo_with_no_tags_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_no_tags_tag_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add much more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: more text") - - return git_repo + if not cached_repo_with_no_tags_tag_commits.exists(): + raise RuntimeError("Unable to find cached repo!") + copy_dir_tree(cached_repo_with_no_tags_tag_commits, example_project_dir) + return example_project_git_repo() diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index f90f82ce7..165fd4809 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -3,180 +3,300 @@ from typing import TYPE_CHECKING import pytest +from git import Repo from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file +from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory if TYPE_CHECKING: - from git import Repo + from pathlib import Path + from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UseParserFn - from tests.fixtures.git_repo import RepoInitFn + from tests.fixtures.git_repo import ( + BuildRepoFn, + CommitConvention, + CommitMsg, + ExProjectGitRepoFn, + GetRepoDefinitionFn, + GetVersionStringsFn, + VersionStr, + ) + + +@pytest.fixture(scope="session") +def get_commits_for_trunk_only_repo_w_prerelease_tags() -> GetRepoDefinitionFn: + base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { + "0.1.0": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + } + ], + "0.1.1-rc.1": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + } + ], + "0.2.0-rc.1": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + } + ], + "0.2.0": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + } + ], + } + + def _get_commits_for_trunk_only_repo_w_prerelease_tags( + commit_type: CommitConvention = "angular", + ) -> Mapping[VersionStr, list[CommitMsg]]: + definition: Mapping[VersionStr, list[CommitMsg]] = {} + for version, commits in base_definition.items(): + definition[version] = [ + message_dict[commit_type] for message_dict in commits + ] + return definition + + return _get_commits_for_trunk_only_repo_w_prerelease_tags + + +@pytest.fixture(scope="session") +def get_versions_for_trunk_only_repo_w_prerelease_tags( + get_commits_for_trunk_only_repo_w_prerelease_tags: GetRepoDefinitionFn, +) -> GetVersionStringsFn: + def _get_versions_for_trunk_only_repo_w_prerelease_tags() -> list[VersionStr]: + return list( + get_commits_for_trunk_only_repo_w_prerelease_tags().keys() + ) + + return _get_versions_for_trunk_only_repo_w_prerelease_tags + + +@pytest.fixture(scope="session") +def build_trunk_only_repo_w_prerelease_tags( + get_commits_for_trunk_only_repo_w_prerelease_tags: GetRepoDefinitionFn, + cached_example_git_project: Path, + use_angular_parser: UseParserFn, + use_emoji_parser: UseParserFn, + use_scipy_parser: UseParserFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + default_tag_format_str: str, +) -> BuildRepoFn: + def _build_trunk_only_repo_w_prerelease_tags( + git_repo_path: Path | str, + commit_type: CommitConvention, + tag_format_str: str | None = None, + ) -> None: + repo_definition = get_commits_for_trunk_only_repo_w_prerelease_tags(commit_type) + tag_format = tag_format_str or default_tag_format_str + versions = list(repo_definition.keys()) + next_version = versions[0] + + if not cached_example_git_project.exists(): + raise RuntimeError("Unable to find cached example git project!") + + copy_dir_tree(cached_example_git_project, git_repo_path) + + with temporary_working_directory(git_repo_path), Repo(".") as git_repo: + if commit_type == "angular": + use_angular_parser() + elif commit_type == "emoji": + use_emoji_parser() + elif commit_type == "scipy": + use_scipy_parser() + elif commit_type == "tag": + use_tag_parser() + else: + raise ValueError(f"Unknown commit type: {commit_type}") + + git_repo.git.commit( + a=True, m=repo_definition[next_version][0] + ) # Initial commit + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + next_version = versions[1] + + # Prepare for a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit( + m=repo_definition[next_version][0] + ) # patch level commit + + # Make a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + next_version = versions[2] + + # Prepare a 2nd prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit( + m=repo_definition[next_version][0] + ) # minor level commit + + # Make a 2nd prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + next_version = versions[3] + + # Prepare for a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit( + m=repo_definition[next_version][0] + ) # minor level commit + + # Make a full release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + return _build_trunk_only_repo_w_prerelease_tags + + +# --------------------------------------------------------------------------- # +# Session-level fixtures to use to set up cached repositories on first use # +# --------------------------------------------------------------------------- # + + +@pytest.fixture(scope="session") +def cached_repo_with_single_branch_and_prereleases_angular_commits( + build_trunk_only_repo_w_prerelease_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_single_branch_and_prereleases_angular_commits.__name__ + ) + build_trunk_only_repo_w_prerelease_tags(cached_repo_path, "angular") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_with_single_branch_and_prereleases_emoji_commits( + build_trunk_only_repo_w_prerelease_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_single_branch_and_prereleases_emoji_commits.__name__ + ) + build_trunk_only_repo_w_prerelease_tags(cached_repo_path, "emoji") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_with_single_branch_and_prereleases_scipy_commits( + build_trunk_only_repo_w_prerelease_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_single_branch_and_prereleases_scipy_commits.__name__ + ) + build_trunk_only_repo_w_prerelease_tags(cached_repo_path, "scipy") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_with_single_branch_and_prereleases_tag_commits( + build_trunk_only_repo_w_prerelease_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_single_branch_and_prereleases_tag_commits.__name__ + ) + build_trunk_only_repo_w_prerelease_tags(cached_repo_path, "tag") + return teardown_cached_dir(cached_repo_path) + + +# --------------------------------------------------------------------------- # +# Test-level fixtures to use to set up temporary test directory # +# --------------------------------------------------------------------------- # @pytest.fixture def repo_with_single_branch_and_prereleases_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_single_branch_and_prereleases_angular_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - return git_repo + if not cached_repo_with_single_branch_and_prereleases_angular_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_with_single_branch_and_prereleases_angular_commits, + example_project_dir, + ) + return example_project_git_repo() @pytest.fixture def repo_with_single_branch_and_prereleases_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_single_branch_and_prereleases_emoji_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - return git_repo + if not cached_repo_with_single_branch_and_prereleases_emoji_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_with_single_branch_and_prereleases_emoji_commits, example_project_dir + ) + return example_project_git_repo() @pytest.fixture def repo_with_single_branch_and_prereleases_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_single_branch_and_prereleases_scipy_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - return git_repo + if not cached_repo_with_single_branch_and_prereleases_scipy_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_with_single_branch_and_prereleases_scipy_commits, example_project_dir + ) + return example_project_git_repo() @pytest.fixture def repo_with_single_branch_and_prereleases_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_single_branch_and_prereleases_tag_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - return git_repo + if not cached_repo_with_single_branch_and_prereleases_tag_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_with_single_branch_and_prereleases_tag_commits, example_project_dir + ) + return example_project_git_repo() diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index 83597265f..4a60731df 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -3,120 +3,236 @@ from typing import TYPE_CHECKING import pytest +from git import Repo from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file +from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory if TYPE_CHECKING: - from git import Repo + from pathlib import Path + from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UseParserFn - from tests.fixtures.git_repo import RepoInitFn + from tests.fixtures.git_repo import ( + BuildRepoFn, + CommitConvention, + CommitMsg, + ExProjectGitRepoFn, + GetRepoDefinitionFn, + GetVersionStringsFn, + VersionStr, + ) + + +@pytest.fixture(scope="session") +def get_commits_for_trunk_only_repo_w_tags() -> GetRepoDefinitionFn: + base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { + "0.1.0": [{ + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + }], + "0.1.1": [{ + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + }], + } + def _get_commits_for_trunk_only_repo_w_tags( + commit_type: CommitConvention = "angular" + ) -> Mapping[VersionStr, list[CommitMsg]]: + definition: Mapping[VersionStr, list[CommitMsg]] = {} + for version, commits in base_definition.items(): + definition[version] = [ + message_dict[commit_type] for message_dict in commits + ] + return definition + + return _get_commits_for_trunk_only_repo_w_tags + + +@pytest.fixture(scope="session") +def get_versions_for_trunk_only_repo_w_tags( + get_commits_for_trunk_only_repo_w_tags: GetRepoDefinitionFn, +) -> GetVersionStringsFn: + def _get_versions_for_trunk_only_repo_w_tags() -> list[VersionStr]: + return list( + get_commits_for_trunk_only_repo_w_tags().keys() + ) + + return _get_versions_for_trunk_only_repo_w_tags + + +@pytest.fixture(scope="session") +def build_trunk_only_repo_w_tags( + get_commits_for_trunk_only_repo_w_tags: GetRepoDefinitionFn, + cached_example_git_project: Path, + use_angular_parser: UseParserFn, + use_emoji_parser: UseParserFn, + use_scipy_parser: UseParserFn, + use_tag_parser: UseParserFn, + file_in_repo: str, + default_tag_format_str: str, +) -> BuildRepoFn: + def _build_trunk_only_repo_w_tags( + git_repo_path: Path | str, + commit_type: CommitConvention, + tag_format_str: str | None = None, + ) -> None: + repo_definition = get_commits_for_trunk_only_repo_w_tags(commit_type) + tag_format = tag_format_str or default_tag_format_str + versions = list(repo_definition.keys()) + next_version = versions[0] + + if not cached_example_git_project.exists(): + raise RuntimeError("Unable to find example git project!") + + copy_dir_tree(cached_example_git_project, git_repo_path) + + with temporary_working_directory(git_repo_path), Repo(".") as git_repo: + if commit_type == "angular": + use_angular_parser() + elif commit_type == "emoji": + use_emoji_parser() + elif commit_type == "scipy": + use_scipy_parser() + elif commit_type == "tag": + use_tag_parser() + else: + raise ValueError(f"Unknown commit type: {commit_type}") + + git_repo.git.commit(a=True, m=repo_definition[next_version][0]) # Initial commit + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + next_version = versions[1] + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) # patch level message + + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + return _build_trunk_only_repo_w_tags + + +# --------------------------------------------------------------------------- # +# Session-level fixtures to use to set up cached repositories on first use # +# --------------------------------------------------------------------------- # + + +@pytest.fixture(scope="session") +def cached_repo_with_single_branch_angular_commits( + build_trunk_only_repo_w_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_single_branch_angular_commits.__name__ + ) + build_trunk_only_repo_w_tags(cached_repo_path, "angular") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_with_single_branch_emoji_commits( + build_trunk_only_repo_w_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_single_branch_emoji_commits.__name__ + ) + build_trunk_only_repo_w_tags(cached_repo_path, "emoji") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_with_single_branch_scipy_commits( + build_trunk_only_repo_w_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_single_branch_scipy_commits.__name__ + ) + build_trunk_only_repo_w_tags(cached_repo_path, "scipy") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_with_single_branch_tag_commits( + build_trunk_only_repo_w_tags: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_with_single_branch_tag_commits.__name__ + ) + build_trunk_only_repo_w_tags(cached_repo_path, "tag") + return teardown_cached_dir(cached_repo_path) + + +# --------------------------------------------------------------------------- # +# Test-level fixtures to use to set up temporary test directory # +# --------------------------------------------------------------------------- # @pytest.fixture def repo_with_single_branch_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_single_branch_angular_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) - git_repo.git.tag("v0.1.1", m="v0.1.1") - - assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - return git_repo + if not cached_repo_with_single_branch_angular_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree(cached_repo_with_single_branch_angular_commits, example_project_dir) + return example_project_git_repo() @pytest.fixture def repo_with_single_branch_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_single_branch_emoji_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) - git_repo.git.tag("v0.1.1", m="v0.1.1") - - assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - return git_repo + if not cached_repo_with_single_branch_emoji_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree(cached_repo_with_single_branch_emoji_commits, example_project_dir) + return example_project_git_repo() @pytest.fixture def repo_with_single_branch_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_single_branch_scipy_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) - git_repo.git.tag("v0.1.1", m="v0.1.1") - - assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - return git_repo + if not cached_repo_with_single_branch_scipy_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree(cached_repo_with_single_branch_scipy_commits, example_project_dir) + return example_project_git_repo() @pytest.fixture def repo_with_single_branch_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + cached_repo_with_single_branch_tag_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1")) - git_repo.git.tag("v0.1.1", m="v0.1.1") - - assert git_repo.commit("v0.1.1").hexsha == git_repo.head.commit.hexsha - return git_repo + if not cached_repo_with_single_branch_tag_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree(cached_repo_with_single_branch_tag_commits, example_project_dir) + return example_project_git_repo() From 3db33f0c6556701de54ecc07dde91b65fc5d60a5 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 12 Jan 2024 18:44:30 -0500 Subject: [PATCH 070/167] test(fixtures): add caching to git flow development repos --- .../git_flow/repo_w_2_release_channels.py | 680 +++++++------- .../git_flow/repo_w_3_release_channels.py | 828 +++++++++--------- 2 files changed, 795 insertions(+), 713 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 93de4e310..beb3e37cf 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -3,339 +3,411 @@ from typing import TYPE_CHECKING import pytest +from git import Repo from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file +from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory if TYPE_CHECKING: - from git import Repo + from pathlib import Path + from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn - from tests.fixtures.git_repo import RepoInitFn + from tests.fixtures.git_repo import ( + BuildRepoFn, + CommitConvention, + CommitMsg, + ExProjectGitRepoFn, + GetRepoDefinitionFn, + GetVersionStringsFn, + VersionStr, + ) -@pytest.fixture -def repo_with_git_flow_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, +@pytest.fixture(scope="session") +def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinitionFn: + base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { + "0.1.0": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + } + ], + "0.1.1-rc.1": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + } + ], + "1.0.0-rc.1": [ + { + "angular": "feat!: add some more text", + "emoji": ":boom: add some more text", + "scipy": "API: add some more text", + "tag": ":sparkles: add some more text\n\nBREAKING CHANGE: add some more text", + } + ], + "1.0.0": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + }, + ], + "1.1.0": [ + { + "angular": "feat: (dev) add some more text", + "emoji": ":sparkles: (dev) add some more text", + "scipy": "ENH: (dev) add some more text", + "tag": ":sparkles: (dev) add some more text", + }, + ], + "1.1.1": [ + { + "angular": "fix: (dev) add some more text", + "emoji": ":bug: (dev) add some more text", + "scipy": "MAINT: (dev) add some more text", + "tag": ":nut_and_bolt: (dev) add some more text", + }, + ], + "1.2.0-alpha.1": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + }, + ], + "1.2.0-alpha.2": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + }, + { + "angular": "fix: (feature) add some missing text", + "emoji": ":bug: (feature) add some missing text", + "scipy": "MAINT: (feature) add some missing text", + "tag": ":nut_and_bolt: (feature) add some missing text", + }, + ], + } + + def _get_commits_for_git_flow_repo_with_2_release_channels( + commit_type: CommitConvention = "angular", + ) -> Mapping[VersionStr, list[CommitMsg]]: + definition: Mapping[VersionStr, list[CommitMsg]] = {} + for version, commits in base_definition.items(): + definition[version] = [ + message_dict[commit_type] for message_dict in commits + ] + return definition + + return _get_commits_for_git_flow_repo_with_2_release_channels + + +@pytest.fixture(scope="session") +def get_versions_for_git_flow_repo_with_2_release_channels( + get_commits_for_git_flow_repo_with_2_release_channels: GetRepoDefinitionFn, +) -> GetVersionStringsFn: + def _get_versions_for_git_flow_repo_with_2_release_channels() -> list[VersionStr]: + return list(get_commits_for_git_flow_repo_with_2_release_channels().keys()) + + return _get_versions_for_git_flow_repo_with_2_release_channels + + +@pytest.fixture(scope="session") +def build_git_flow_repo_with_2_release_channels( + get_commits_for_git_flow_repo_with_2_release_channels: GetRepoDefinitionFn, + cached_example_git_project: Path, update_pyproject_toml: UpdatePyprojectTomlFn, + use_angular_parser: UseParserFn, + use_emoji_parser: UseParserFn, + use_scipy_parser: UseParserFn, + use_tag_parser: UseParserFn, file_in_repo: str, + default_tag_format_str: str, +) -> BuildRepoFn: + def _build_git_flow_repo_with_2_release_channels( + git_repo_path: Path | str, + commit_type: CommitConvention, + tag_format_str: str | None = None, + ) -> None: + repo_definition = get_commits_for_git_flow_repo_with_2_release_channels( + commit_type + ) + tag_format = tag_format_str or default_tag_format_str + versions = list(repo_definition.keys()) + next_version = versions[0] + + if not cached_example_git_project.exists(): + raise RuntimeError("Unable to find cached example git project!") + + copy_dir_tree(cached_example_git_project, git_repo_path) + + with temporary_working_directory(git_repo_path), Repo(".") as git_repo: + update_pyproject_toml( + "tool.semantic_release.branches.features", + { + "match": "feat.*", + "prerelease": True, + "prerelease_token": "alpha", + }, + ) + + if commit_type == "angular": + use_angular_parser() + elif commit_type == "emoji": + use_emoji_parser() + elif commit_type == "scipy": + use_scipy_parser() + elif commit_type == "tag": + use_tag_parser() + else: + raise ValueError(f"Unknown commit type: {commit_type}") + + git_repo.git.commit( + a=True, m=repo_definition[next_version][0] # Initial commit + ) + + # Make initial feature release (v0.1.0) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[1] + + # Prepare for a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a patch level release candidate (v0.1.1-rc.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[2] + + # Prepare for a major feature release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a major feature release candidate (v1.0.0-rc.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[3] + + # Prepare for a major feature release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a major feature release (v1.0.0) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[4] + + # Change to a dev branch + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + # Prepare for a minor feature release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a minor feature release (v1.1.0) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[5] + + # Prepare for a patch level release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a patch level release (v1.1.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[6] + + # Change to a feature branch + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Prepare for a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make an alpha prerelease (v1.2.0-alpha.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[7] + + # Prepare for a 2nd prerelease with 2 commits + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][1]) + + # Make a 2nd alpha prerelease (v1.2.0-alpha.2) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + return _build_git_flow_repo_with_2_release_channels + + +# --------------------------------------------------------------------------- # +# Session-level fixtures to use to set up cached repositories on first use # +# --------------------------------------------------------------------------- # + + +@pytest.fixture(scope="session") +def cached_repo_w_git_flow_n_2_release_channels_angular_commits( + build_git_flow_repo_with_2_release_channels: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_git_flow_n_2_release_channels_angular_commits.__name__ + ) + build_git_flow_repo_with_2_release_channels(cached_repo_path, "angular") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_git_flow_n_2_release_channels_emoji_commits( + build_git_flow_repo_with_2_release_channels: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_git_flow_n_2_release_channels_emoji_commits.__name__ + ) + build_git_flow_repo_with_2_release_channels(cached_repo_path, "emoji") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_git_flow_n_2_release_channels_scipy_commits( + build_git_flow_repo_with_2_release_channels: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_git_flow_n_2_release_channels_scipy_commits.__name__ + ) + build_git_flow_repo_with_2_release_channels(cached_repo_path, "scipy") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_git_flow_n_2_release_channels_tag_commits( + build_git_flow_repo_with_2_release_channels: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_git_flow_n_2_release_channels_tag_commits.__name__ + ) + build_git_flow_repo_with_2_release_channels(cached_repo_path, "tag") + return teardown_cached_dir(cached_repo_path) + + +# --------------------------------------------------------------------------- # +# Test-level fixtures to use to set up temporary test directory # +# --------------------------------------------------------------------------- # + + +@pytest.fixture +def repo_with_git_flow_angular_commits( + cached_repo_w_git_flow_n_2_release_channels_angular_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - update_pyproject_toml( - "tool.semantic_release.branches.features", - {"match": "feat.*", "prerelease": True, "prerelease_token": "alpha"}, + if not cached_repo_w_git_flow_n_2_release_channels_angular_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_git_flow_n_2_release_channels_angular_commits, + example_project_dir, ) - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat!: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) - git_repo.git.tag("v1.1.0", m="v1.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) - git_repo.git.tag("v1.1.1", m="v1.1.1") - - assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) - git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (feature) add some missing text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) - git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") - - assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo + return example_project_git_repo() @pytest.fixture def repo_with_git_flow_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, + cached_repo_w_git_flow_n_2_release_channels_emoji_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":boom: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) - git_repo.git.tag("v1.1.0", m="v1.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) - git_repo.git.tag("v1.1.1", m="v1.1.1") - - assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) - git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: (feature) add some missing text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) - git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") - - assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo + if not cached_repo_w_git_flow_n_2_release_channels_emoji_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_git_flow_n_2_release_channels_emoji_commits, + example_project_dir, + ) + return example_project_git_repo() @pytest.fixture def repo_with_git_flow_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, + cached_repo_w_git_flow_n_2_release_channels_scipy_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="API: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) - git_repo.git.tag("v1.1.0", m="v1.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) - git_repo.git.tag("v1.1.1", m="v1.1.1") - - assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) - git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: (feature) add some missing text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) - git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") - - assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo + if not cached_repo_w_git_flow_n_2_release_channels_scipy_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_git_flow_n_2_release_channels_scipy_commits, + example_project_dir, + ) + return example_project_git_repo() @pytest.fixture def repo_with_git_flow_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + cached_repo_w_git_flow_n_2_release_channels_tag_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit( - m=":sparkles: add some more text\n\nBREAKING CHANGE: add some more text" + if not cached_repo_w_git_flow_n_2_release_channels_tag_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_git_flow_n_2_release_channels_tag_commits, + example_project_dir, ) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0")) - git_repo.git.tag("v1.1.0", m="v1.1.0") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.1")) - git_repo.git.tag("v1.1.1", m="v1.1.1") - - assert git_repo.commit("v1.1.1").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.1")) - git_repo.git.tag("v1.2.0-alpha.1", m="v1.2.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: (feature) add some missing text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.2.0-alpha.2")) - git_repo.git.tag("v1.2.0-alpha.2", m="v1.2.0-alpha.2") - - assert git_repo.commit("v1.2.0-alpha.2").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo + return example_project_git_repo() diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index 23de6346f..c379649c6 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -3,449 +3,459 @@ from typing import TYPE_CHECKING import pytest +from git import Repo from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file +from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory if TYPE_CHECKING: - from git import Repo + from pathlib import Path + from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn - from tests.fixtures.git_repo import RepoInitFn + from tests.fixtures.git_repo import ( + BuildRepoFn, + CommitConvention, + CommitMsg, + ExProjectGitRepoFn, + GetRepoDefinitionFn, + GetVersionStringsFn, + VersionStr, + ) -@pytest.fixture -def repo_with_git_flow_and_release_channels_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, +@pytest.fixture(scope="session") +def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: + base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { + "0.1.0": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + }, + ], + "0.1.1-rc.1": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + }, + ], + "1.0.0-rc.1": [ + { + "angular": "feat!: add some more text", + "emoji": ":boom: add some more text", + "scipy": "API: add some more text", + "tag": ":sparkles: add some more text\n\nBREAKING CHANGE: add some more text", + }, + ], + "1.0.0": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + }, + ], + "1.1.0-rc.1": [ + { + "angular": "feat: (dev) add some more text", + "emoji": ":sparkles: (dev) add some more text", + "scipy": "ENH: (dev) add some more text", + "tag": ":sparkles: (dev) add some more text", + }, + ], + "1.1.0-rc.2": [ + { + "angular": "fix: (dev) add some more text", + "emoji": ":bug: (dev) add some more text", + "scipy": "MAINT: (dev) add some more text", + "tag": ":nut_and_bolt: (dev) add some more text", + }, + ], + "1.1.0-alpha.1": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + }, + ], + "1.1.0-alpha.2": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + }, + ], + "1.1.0-alpha.3": [ + { + "angular": "fix: (feature) add some more text", + "emoji": ":bug: (feature) add some more text", + "scipy": "MAINT: (feature) add some more text", + "tag": ":nut_and_bolt: (feature) add some more text", + }, + ], + } + + def _get_commits_for_git_flow_repo_w_3_release_channels( + commit_type: CommitConvention = "angular", + ) -> Mapping[VersionStr, list[CommitMsg]]: + definition: Mapping[VersionStr, list[CommitMsg]] = {} + for version, commits in base_definition.items(): + definition[version] = [ + message_dict[commit_type] for message_dict in commits + ] + return definition + + return _get_commits_for_git_flow_repo_w_3_release_channels + + +@pytest.fixture(scope="session") +def get_versions_for_git_flow_repo_w_3_release_channels( + get_commits_for_git_flow_repo_w_3_release_channels: GetRepoDefinitionFn, +) -> GetVersionStringsFn: + def _get_versions_for_git_flow_repo_w_3_release_channels() -> list[VersionStr]: + return list(get_commits_for_git_flow_repo_w_3_release_channels().keys()) + + return _get_versions_for_git_flow_repo_w_3_release_channels + + +@pytest.fixture(scope="session") +def build_git_flow_repo_w_3_release_channels( + get_commits_for_git_flow_repo_w_3_release_channels: GetRepoDefinitionFn, + cached_example_git_project: Path, update_pyproject_toml: UpdatePyprojectTomlFn, + use_angular_parser: UseParserFn, + use_emoji_parser: UseParserFn, + use_scipy_parser: UseParserFn, + use_tag_parser: UseParserFn, file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - update_pyproject_toml( - "tool.semantic_release.branches.features", - {"match": "feat.*", "prerelease": True, "prerelease_token": "alpha"}, + default_tag_format_str: str, +) -> BuildRepoFn: + def _build_git_flow_repo_w_3_release_channels( + git_repo_path: Path | str, + commit_type: CommitConvention, + tag_format_str: str | None = None, + ) -> None: + repo_definition = get_commits_for_git_flow_repo_w_3_release_channels( + commit_type + ) + tag_format = tag_format_str or default_tag_format_str + versions = list(repo_definition.keys()) + next_version = versions[0] + + if not cached_example_git_project.exists(): + raise RuntimeError("Unable to find cached example git project!") + + copy_dir_tree(cached_example_git_project, git_repo_path) + + with temporary_working_directory(git_repo_path), Repo(".") as git_repo: + update_pyproject_toml( + "tool.semantic_release.branches.dev", + {"match": "dev", "prerelease": True, "prerelease_token": "rc"}, + ) + update_pyproject_toml( + "tool.semantic_release.branches.features", + { + "match": "feat.*", + "prerelease": True, + "prerelease_token": "alpha", + }, + ) + + if commit_type == "angular": + use_angular_parser() + elif commit_type == "emoji": + use_emoji_parser() + elif commit_type == "scipy": + use_scipy_parser() + elif commit_type == "tag": + use_tag_parser() + else: + raise ValueError(f"Unknown commit type: {commit_type}") + + git_repo.git.commit( + a=True, m=repo_definition[next_version][0] # Initial commit + ) + + # Make initial feature release (v0.1.0) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[1] + + # Prepare for a prerelease + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a patch level release candidate (v0.1.1-rc.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[2] + + # Prepare for a major feature release + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a major feature release candidate (v1.0.0-rc.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[3] + + # Add non-breaking feature commit + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a major feature release (v1.0.0) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[4] + + # Change to a dev branch + git_repo.create_head("dev") + git_repo.heads.dev.checkout() + + # Prepare for a minor bump release candidate + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a minor bump release candidate (v1.1.0-rc.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[5] + + # Make a patch level commit + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a 2nd release candidate (v1.1.0-rc.2) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[6] + + # Change to a feature branch + git_repo.create_head("feature") + git_repo.heads.feature.checkout() + + # Make a feature commit + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make an alpha prerelease (v1.1.0-alpha.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[7] + + # Make a another feature commit + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a 2nd alpha prerelease (v1.1.0-alpha.2) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[8] + + # Make a patch level commit + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a 3rd alpha prerelease (v1.1.0-alpha.3) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + return _build_git_flow_repo_w_3_release_channels + + +# --------------------------------------------------------------------------- # +# Session-level fixtures to use to set up cached repositories on first use # +# --------------------------------------------------------------------------- # + + +@pytest.fixture(scope="session") +def cached_repo_w_git_flow_n_3_release_channels_angular_commits_tag_format( + build_git_flow_repo_w_3_release_channels: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_git_flow_n_3_release_channels_angular_commits_tag_format.__name__ + ) + build_git_flow_repo_w_3_release_channels( + cached_repo_path, "angular", tag_format_str="vpy{version}" + ) + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_git_flow_n_3_release_channels_angular_commits( + build_git_flow_repo_w_3_release_channels: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_git_flow_n_3_release_channels_angular_commits.__name__ ) + build_git_flow_repo_w_3_release_channels(cached_repo_path, "angular") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_git_flow_n_3_release_channels_emoji_commits( + build_git_flow_repo_w_3_release_channels: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_git_flow_n_3_release_channels_emoji_commits.__name__ + ) + build_git_flow_repo_w_3_release_channels(cached_repo_path, "emoji") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_git_flow_n_3_release_channels_scipy_commits( + build_git_flow_repo_w_3_release_channels: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_git_flow_n_3_release_channels_scipy_commits.__name__ + ) + build_git_flow_repo_w_3_release_channels(cached_repo_path, "scipy") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_git_flow_n_3_release_channels_tag_commits( + build_git_flow_repo_w_3_release_channels: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_git_flow_n_3_release_channels_tag_commits.__name__ + ) + build_git_flow_repo_w_3_release_channels(cached_repo_path, "tag") + return teardown_cached_dir(cached_repo_path) + - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat!: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") - - assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") - - assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo +# --------------------------------------------------------------------------- # +# Test-level fixtures to use to set up temporary test directory # +# --------------------------------------------------------------------------- # @pytest.fixture def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, - file_in_repo: str, + cached_repo_w_git_flow_n_3_release_channels_angular_commits_tag_format: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, + change_to_ex_proj_dir: None, +) -> Repo: + if not cached_repo_w_git_flow_n_3_release_channels_angular_commits_tag_format.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_git_flow_n_3_release_channels_angular_commits_tag_format, + example_project_dir, + ) + return example_project_git_repo() + + +@pytest.fixture +def repo_with_git_flow_and_release_channels_angular_commits( + cached_repo_w_git_flow_n_3_release_channels_angular_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("vpy0.1.0", m="vpy0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("vpy0.1.1-rc.1", m="vpy0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat!: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("vpy1.0.0-rc.1", m="vpy1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("vpy1.0.0", m="vpy1.0.0") - - assert git_repo.commit("vpy1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("vpy1.1.0-rc.1", m="vpy1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("vpy1.1.0-rc.2", m="vpy1.1.0-rc.2") - - assert git_repo.commit("vpy1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("vpy1.1.0-alpha.1", m="vpy1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("vpy1.1.0-alpha.2", m="vpy1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("vpy1.1.0-alpha.3", m="vpy1.1.0-alpha.3") - - assert git_repo.commit("vpy1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo + if not cached_repo_w_git_flow_n_3_release_channels_angular_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_git_flow_n_3_release_channels_angular_commits, + example_project_dir, + ) + return example_project_git_repo() @pytest.fixture def repo_with_git_flow_and_release_channels_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, + cached_repo_w_git_flow_n_3_release_channels_emoji_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":boom: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") - - assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") - - assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo + if not cached_repo_w_git_flow_n_3_release_channels_emoji_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_git_flow_n_3_release_channels_emoji_commits, + example_project_dir, + ) + return example_project_git_repo() @pytest.fixture def repo_with_git_flow_and_release_channels_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, + cached_repo_w_git_flow_n_3_release_channels_scipy_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="API: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") - - assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") - - assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo + if not cached_repo_w_git_flow_n_3_release_channels_scipy_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_git_flow_n_3_release_channels_scipy_commits, + example_project_dir, + ) + return example_project_git_repo() @pytest.fixture def repo_with_git_flow_and_release_channels_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + cached_repo_w_git_flow_n_3_release_channels_tag_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit( - m=":sparkles: add some more text\n\nBREAKING CHANGE: add some more text" + if not cached_repo_w_git_flow_n_3_release_channels_tag_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_git_flow_n_3_release_channels_tag_commits, + example_project_dir, ) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0-rc.1")) - git_repo.git.tag("v1.0.0-rc.1", m="v1.0.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.0.0")) - git_repo.git.tag("v1.0.0", m="v1.0.0") - - assert git_repo.commit("v1.0.0").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "dev" has prerelease suffix of "rc" - git_repo.create_head("dev") - git_repo.heads.dev.checkout() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.1")) - git_repo.git.tag("v1.1.0-rc.1", m="v1.1.0-rc.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: (dev) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-rc.2")) - git_repo.git.tag("v1.1.0-rc.2", m="v1.1.0-rc.2") - - assert git_repo.commit("v1.1.0-rc.2").hexsha == git_repo.head.commit.hexsha - - # Suppose branch "feature" has prerelease suffix of "alpha" - git_repo.create_head("feature") - git_repo.heads.feature.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.1")) - git_repo.git.tag("v1.1.0-alpha.1", m="v1.1.0-alpha.1") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.2")) - git_repo.git.tag("v1.1.0-alpha.2", m="v1.1.0-alpha.2") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="1.1.0-alpha.3")) - git_repo.git.tag("v1.1.0-alpha.3", m="v1.1.0-alpha.3") - - assert git_repo.commit("v1.1.0-alpha.3").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "feature" - return git_repo + return example_project_git_repo() From 693b14352da4fa2c7fcd969c4b8c5230ec90055c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 12 Jan 2024 18:45:02 -0500 Subject: [PATCH 071/167] test(fixtures): add caching to github flow development repos --- .../github_flow/repo_w_release_channels.py | 503 +++++++++++------- 1 file changed, 299 insertions(+), 204 deletions(-) diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 2eb66de0e..fb738acf8 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -3,237 +3,332 @@ from typing import TYPE_CHECKING import pytest +from git import Repo from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file +from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory if TYPE_CHECKING: - from git import Repo + from pathlib import Path + from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn - from tests.fixtures.git_repo import RepoInitFn + from tests.fixtures.git_repo import ( + BuildRepoFn, + CommitConvention, + CommitMsg, + ExProjectGitRepoFn, + GetRepoDefinitionFn, + GetVersionStringsFn, + VersionStr, + ) -@pytest.fixture -def repo_with_main_and_feature_branches_angular_commits( - git_repo_factory: RepoInitFn, - use_angular_parser: UseParserFn, +@pytest.fixture(scope="session") +def get_commits_for_github_flow_repo_w_feature_release_channel() -> GetRepoDefinitionFn: + base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { + "0.1.0": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + }, + ], + "0.1.1-rc.1": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + } + ], + "0.2.0-rc.1": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + } + ], + "0.2.0": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + } + ], + "0.3.0-beta.1": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + } + ], + } + + def _get_commits_for_github_flow_repo_w_feature_release_channel( + commit_type: CommitConvention = "angular", + ) -> Mapping[VersionStr, list[CommitMsg]]: + definition: Mapping[VersionStr, list[CommitMsg]] = {} + for version, commits in base_definition.items(): + definition[version] = [ + message_dict[commit_type] for message_dict in commits + ] + return definition + + return _get_commits_for_github_flow_repo_w_feature_release_channel + + +@pytest.fixture(scope="session") +def get_versions_for_github_flow_repo_w_feature_release_channel( + get_commits_for_github_flow_repo_w_feature_release_channel: GetRepoDefinitionFn, +) -> GetVersionStringsFn: + def _get_versions_for_github_flow_repo_w_feature_release_channel() -> list[VersionStr]: + return list( + get_commits_for_github_flow_repo_w_feature_release_channel().keys() + ) + + return _get_versions_for_github_flow_repo_w_feature_release_channel + + +@pytest.fixture(scope="session") +def build_github_flow_repo_w_feature_release_channel( + get_commits_for_github_flow_repo_w_feature_release_channel: GetRepoDefinitionFn, + cached_example_git_project: Path, update_pyproject_toml: UpdatePyprojectTomlFn, + use_angular_parser: UseParserFn, + use_emoji_parser: UseParserFn, + use_scipy_parser: UseParserFn, + use_tag_parser: UseParserFn, file_in_repo: str, - change_to_ex_proj_dir: None, -) -> Repo: - git_repo = git_repo_factory() - use_angular_parser() - update_pyproject_toml( - "tool.semantic_release.branches.beta-testing", - {"match": "beta.*", "prerelease": True, "prerelease_token": "beta"}, + default_tag_format_str: str, +) -> BuildRepoFn: + def _build_github_flow_repo_w_feature_release_channel( + git_repo_path: Path | str, + commit_type: CommitConvention, + tag_format_str: str | None = None, + ) -> None: + repo_definition = get_commits_for_github_flow_repo_w_feature_release_channel(commit_type) + tag_format = tag_format_str or default_tag_format_str + versions = list(repo_definition.keys()) + next_version = versions[0] + + if not cached_example_git_project.exists(): + raise RuntimeError("Unable to find cached files directory!") + + copy_dir_tree(cached_example_git_project, git_repo_path) + + with temporary_working_directory(git_repo_path), Repo(".") as git_repo: + update_pyproject_toml( + "tool.semantic_release.branches.beta-testing", + {"match": "beta.*", "prerelease": True, "prerelease_token": "beta"}, + ) + + if commit_type == "angular": + use_angular_parser() + elif commit_type == "emoji": + use_emoji_parser() + elif commit_type == "scipy": + use_scipy_parser() + elif commit_type == "tag": + use_tag_parser() + else: + raise ValueError(f"Unknown commit type: {commit_type}") + + git_repo.git.commit( + a=True, m=repo_definition[next_version][0] # Initial commit + ) + + # Make initial feature release (v0.1.0) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[1] + + # Make a patch level commit + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a patch level release candidate (v0.1.1-rc.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[2] + + # Make a minor level commit + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a minor level release candidate (v0.2.0-rc.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[3] + + # Make a minor level commit + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a minor level release (v0.2.0) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + # Increment version pointer + next_version = versions[4] + + # Checkout beta_testing branch + git_repo.create_head("beta_testing") + git_repo.heads.beta_testing.checkout() + + # Make a feature level commit + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=repo_definition[next_version][0]) + + # Make a feature level beta release (v0.3.0-beta.1) + add_text_to_file(git_repo, file_in_repo) + git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) + tag_str = tag_format.format(version=next_version) + git_repo.git.tag(tag_str, m=tag_str) + + return _build_github_flow_repo_w_feature_release_channel + + +# --------------------------------------------------------------------------- # +# Session-level fixtures to use to set up cached repositories on first use # +# --------------------------------------------------------------------------- # + + +@pytest.fixture(scope="session") +def cached_repo_w_github_flow_w_feature_release_channel_angular_commits( + build_github_flow_repo_w_feature_release_channel: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_github_flow_w_feature_release_channel_angular_commits.__name__ ) + build_github_flow_repo_w_feature_release_channel(cached_repo_path, "angular") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_github_flow_w_feature_release_channel_emoji_commits( + build_github_flow_repo_w_feature_release_channel: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_github_flow_w_feature_release_channel_emoji_commits.__name__ + ) + build_github_flow_repo_w_feature_release_channel(cached_repo_path, "emoji") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_github_flow_w_feature_release_channel_scipy_commits( + build_github_flow_repo_w_feature_release_channel: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_github_flow_w_feature_release_channel_scipy_commits.__name__ + ) + build_github_flow_repo_w_feature_release_channel(cached_repo_path, "scipy") + return teardown_cached_dir(cached_repo_path) + + +@pytest.fixture(scope="session") +def cached_repo_w_github_flow_w_feature_release_channel_tag_commits( + build_github_flow_repo_w_feature_release_channel: BuildRepoFn, + cached_files_dir: Path, + teardown_cached_dir: TeardownCachedDirFn, +) -> Path: + cached_repo_path = cached_files_dir.joinpath( + cached_repo_w_github_flow_w_feature_release_channel_tag_commits.__name__ + ) + build_github_flow_repo_w_feature_release_channel(cached_repo_path, "tag") + return teardown_cached_dir(cached_repo_path) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="fix: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - git_repo.create_head("beta_testing") - git_repo.heads.beta_testing.checkout() +# --------------------------------------------------------------------------- # +# Test-level fixtures to use to set up temporary test directory # +# --------------------------------------------------------------------------- # - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="feat: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) - git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") - assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "beta_testing" - return git_repo +@pytest.fixture +def repo_w_github_flow_w_feature_release_channel_angular_commits( + cached_repo_w_github_flow_w_feature_release_channel_angular_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, + change_to_ex_proj_dir: None, +) -> Repo: + if not cached_repo_w_github_flow_w_feature_release_channel_angular_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_github_flow_w_feature_release_channel_angular_commits, + example_project_dir, + ) + return example_project_git_repo() @pytest.fixture -def repo_with_main_and_feature_branches_emoji_commits( - git_repo_factory: RepoInitFn, - use_emoji_parser: UseParserFn, - file_in_repo: str, +def repo_w_github_flow_w_feature_release_channel_emoji_commits( + cached_repo_w_github_flow_w_feature_release_channel_emoji_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_emoji_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":bug: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("beta_testing") - git_repo.heads.beta_testing.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) - git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") - - assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "beta_testing" - return git_repo + if not cached_repo_w_github_flow_w_feature_release_channel_emoji_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_github_flow_w_feature_release_channel_emoji_commits, + example_project_dir, + ) + return example_project_git_repo() @pytest.fixture -def repo_with_main_and_feature_branches_scipy_commits( - git_repo_factory: RepoInitFn, - use_scipy_parser: UseParserFn, - file_in_repo: str, +def repo_w_github_flow_w_feature_release_channel_scipy_commits( + cached_repo_w_github_flow_w_feature_release_channel_scipy_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_scipy_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="MAINT: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("beta_testing") - git_repo.heads.beta_testing.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="ENH: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) - git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") - - assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "beta_testing" - return git_repo + if not cached_repo_w_github_flow_w_feature_release_channel_scipy_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_github_flow_w_feature_release_channel_scipy_commits, + example_project_dir, + ) + return example_project_git_repo() @pytest.fixture -def repo_with_main_and_feature_branches_tag_commits( - git_repo_factory: RepoInitFn, - use_tag_parser: UseParserFn, - file_in_repo: str, +def repo_w_github_flow_w_feature_release_channel_tag_commits( + cached_repo_w_github_flow_w_feature_release_channel_tag_commits: Path, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: Path, change_to_ex_proj_dir: None, ) -> Repo: - git_repo = git_repo_factory() - use_tag_parser() - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m="Initial commit") - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.0")) - git_repo.git.tag("v0.1.0", m="v0.1.0") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":nut_and_bolt: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.1.1-rc.1")) - git_repo.git.tag("v0.1.1-rc.1", m="v0.1.1-rc.1") - - # Do a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0-rc.1")) - git_repo.git.tag("v0.2.0-rc.1", m="v0.2.0-rc.1") - - # Do a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.2.0")) - git_repo.git.tag("v0.2.0", m="v0.2.0") - - assert git_repo.commit("v0.2.0").hexsha == git_repo.head.commit.hexsha - - git_repo.create_head("beta_testing") - git_repo.heads.beta_testing.checkout() - - # Do a prerelease on the branch - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=":sparkles: (feature) add some more text") - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version="0.3.0-beta.1")) - git_repo.git.tag("v0.3.0-beta.1", m="v0.3.0-beta.1") - - assert git_repo.commit("v0.3.0-beta.1").hexsha == git_repo.head.commit.hexsha - assert git_repo.active_branch.name == "beta_testing" - return git_repo + if not cached_repo_w_github_flow_w_feature_release_channel_tag_commits.exists(): + raise RuntimeError("Unable to find cached repository!") + copy_dir_tree( + cached_repo_w_github_flow_w_feature_release_channel_tag_commits, + example_project_dir, + ) + return example_project_git_repo() From f006e2b2e9e2025a3b07d14d79714420d59a63fe Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 12 Jan 2024 20:54:21 -0500 Subject: [PATCH 072/167] test(fixtures): rename repo main and feature branches fixture across tests --- tests/command_line/test_changelog.py | 4 ++-- tests/command_line/test_version.py | 10 +++++----- tests/scenario/test_next_version.py | 8 ++++---- tests/scenario/test_release_history.py | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 5ac8bf14f..8640a5463 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -41,7 +41,7 @@ lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), "v0.2.0", ), - (lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), "v0.2.0"), + (lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), "v0.2.0"), (lazy_fixture("repo_with_git_flow_angular_commits"), "v1.0.0"), ( lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), @@ -102,7 +102,7 @@ def test_changelog_noop_is_noop( lazy_fixture("repo_with_no_tags_angular_commits"), lazy_fixture("repo_with_single_branch_angular_commits"), lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), + lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), lazy_fixture("repo_with_git_flow_angular_commits"), lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 5a44861bb..e23884aaf 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -43,7 +43,7 @@ lazy_fixture("repo_with_no_tags_angular_commits"), lazy_fixture("repo_with_single_branch_angular_commits"), lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), + lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), lazy_fixture("repo_with_git_flow_angular_commits"), lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], @@ -149,7 +149,7 @@ def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_r ], *[ ( - lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), + lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), cli_args, expected_stdout, ) @@ -250,7 +250,7 @@ def test_version_print( # so excluding lazy_fixture("repo_with_no_tags_angular_commits"), lazy_fixture("repo_with_single_branch_angular_commits"), lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), + lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), lazy_fixture("repo_with_git_flow_angular_commits"), lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], @@ -329,7 +329,7 @@ def test_version_already_released_no_push(repo, cli_runner): ], *[ ( - lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), + lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), cli_args, expected_stdout, ) @@ -469,7 +469,7 @@ def test_version_no_push_force_level( [ lazy_fixture("repo_with_single_branch_angular_commits"), lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), + lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), lazy_fixture("repo_with_git_flow_angular_commits"), lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index 6f25e8600..3c3907c11 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -487,7 +487,7 @@ def test_algorithm_no_zero_dot_versions_tag( # The last full release version was 0.2.0, so it's had a minor # prerelease ( - "repo_with_main_and_feature_branches_angular_commits", + "repo_w_github_flow_w_feature_release_channel_angular_commits", "default_angular_parser", VersionTranslator(prerelease_token="beta"), ): [ @@ -658,7 +658,7 @@ def test_algorithm_with_zero_dot_versions_angular( # The last full release version was 0.2.0, so it's had a minor # prerelease ( - "repo_with_main_and_feature_branches_emoji_commits", + "repo_w_github_flow_w_feature_release_channel_emoji_commits", "default_emoji_parser", VersionTranslator(prerelease_token="beta"), ): [ @@ -859,7 +859,7 @@ def test_algorithm_with_zero_dot_versions_emoji( # The last full release version was 0.2.0, so it's had a minor # prerelease ( - "repo_with_main_and_feature_branches_scipy_commits", + "repo_w_github_flow_w_feature_release_channel_scipy_commits", "default_scipy_parser", VersionTranslator(prerelease_token="beta"), ): [ @@ -1050,7 +1050,7 @@ def test_algorithm_with_zero_dot_versions_scipy( # The last full release version was 0.2.0, so it's had a minor # prerelease ( - "repo_with_main_and_feature_branches_tag_commits", + "repo_w_github_flow_w_feature_release_channel_tag_commits", "default_tag_parser", VersionTranslator(prerelease_token="beta"), ): [ diff --git a/tests/scenario/test_release_history.py b/tests/scenario/test_release_history.py index 3fdc75bc7..3cd1b4d04 100644 --- a/tests/scenario/test_release_history.py +++ b/tests/scenario/test_release_history.py @@ -199,7 +199,7 @@ class FakeReleaseHistoryElements(NamedTuple): REPO_WITH_SINGLE_BRANCH_AND_PRERELEASES_EXPECTED_RELEASE_HISTORY, ), ( - lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), + lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), REPO_WITH_MAIN_AND_FEATURE_BRANCHES_EXPECTED_RELEASE_HISTORY, ), ( @@ -271,7 +271,7 @@ def test_release_history( lazy_fixture("repo_with_no_tags_angular_commits"), lazy_fixture("repo_with_single_branch_angular_commits"), lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), + lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), lazy_fixture("repo_with_git_flow_angular_commits"), lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], @@ -311,7 +311,7 @@ def test_release_history_releases(repo, default_angular_parser): lazy_fixture("repo_with_no_tags_angular_commits"), lazy_fixture("repo_with_single_branch_angular_commits"), lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("repo_with_main_and_feature_branches_angular_commits"), + lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), lazy_fixture("repo_with_git_flow_angular_commits"), lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], From 180a053a992c0ffa03e1d9800cc4bf743d6331b2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 12 Jan 2024 23:52:39 -0500 Subject: [PATCH 073/167] test(util): add util func for removing readonly .git/* files --- tests/command_line/test_changelog.py | 10 +++++----- tests/command_line/test_version.py | 9 +++++---- tests/conftest.py | 4 ++-- tests/util.py | 21 +++++++++++++++++++-- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 8640a5463..d6813b365 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -19,7 +19,7 @@ EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, ) -from tests.util import flatten_dircmp, get_release_history_from_context +from tests.util import flatten_dircmp, get_release_history_from_context, remove_dir_tree if TYPE_CHECKING: from pathlib import Path @@ -60,7 +60,7 @@ def test_changelog_noop_is_noop( ): args = [arg0, tag] if tag and arg0 else [] tempdir = tmp_path_factory.mktemp("test_noop") - shutil.rmtree(str(tempdir.resolve())) + remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) # Set up a requests HTTP session so we can catch the HTTP calls and ensure @@ -115,7 +115,7 @@ def test_changelog_content_regenerated( cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_changelog") - shutil.rmtree(str(tempdir.resolve())) + remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) # Remove the changelog and then check that we can regenerate it @@ -142,7 +142,7 @@ def test_changelog_release_tag_not_in_history( cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_changelog") - shutil.rmtree(str(tempdir.resolve())) + remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) result = cli_runner.invoke(main, [changelog.name or "changelog", *args]) @@ -160,7 +160,7 @@ def test_changelog_post_to_release( cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_changelog") - shutil.rmtree(str(tempdir.resolve())) + remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) # Set up a requests HTTP session so we can catch the HTTP calls and ensure they're diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index e23884aaf..b9d4607ab 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -19,6 +19,7 @@ actions_output_to_dict, flatten_dircmp, get_release_history_from_context, + remove_dir_tree, ) if TYPE_CHECKING: @@ -58,7 +59,7 @@ def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_r repo.git.commit(m="feat: temp new file") tempdir = tmp_path_factory.mktemp("test_noop") - shutil.rmtree(str(tempdir.resolve())) + remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) head_before = repo.head.commit @@ -224,7 +225,7 @@ def test_version_print( repo.git.commit(m="fix: temp new file") tempdir = tmp_path_factory.mktemp("test_version_print") - shutil.rmtree(str(tempdir.resolve())) + remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) @@ -399,7 +400,7 @@ def test_version_no_push_force_level( cli_runner: CliRunner, ): tempdir = tmp_path_factory.mktemp("test_version") - shutil.rmtree(str(tempdir.resolve())) + remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) @@ -664,7 +665,7 @@ def test_version_only_update_files_no_git_actions( # Arrange expected_new_version = "0.3.0" tempdir = tmp_path_factory.mktemp("test_version") - shutil.rmtree(str(tempdir.resolve())) + remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir), dst=tempdir) head_before = runtime_context_with_tags.repo.head.commit diff --git a/tests/conftest.py b/tests/conftest.py index 9ef49b72b..05f70cbff 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,13 +1,13 @@ """Note: fixtures are stored in the tests/fixtures directory for better organisation""" from __future__ import annotations -import shutil from pathlib import Path from typing import TYPE_CHECKING import pytest from tests.fixtures import * +from tests.util import remove_dir_tree if TYPE_CHECKING: from typing import Generator, Protocol @@ -36,4 +36,4 @@ def _teardown_cached_dir(directory: Path | str) -> Path: # clean up any registered cached directories for directory in directories: if directory.exists(): - shutil.rmtree(str(directory)) + remove_dir_tree(directory, force=True) diff --git a/tests/util.py b/tests/util.py index aa08582fc..9415cdc1c 100644 --- a/tests/util.py +++ b/tests/util.py @@ -3,9 +3,9 @@ import os import secrets import shutil +import stat import string -from contextlib import contextmanager -from pathlib import Path +from contextlib import contextmanager, suppress from tempfile import NamedTemporaryFile from typing import TYPE_CHECKING, Tuple @@ -19,6 +19,7 @@ if TYPE_CHECKING: import filecmp + from pathlib import Path from typing import Any, Generator, Iterable, TypeVar try: @@ -47,6 +48,22 @@ def copy_dir_tree(src_dir: Path | str, dst_dir: Path | str) -> None: ) +def remove_dir_tree(directory: Path | str = ".", force: bool = False) -> None: + """ + Compatibility wrapper for shutil.rmtree + + Helpful for deleting directories with .git/* files, which usually have some + read-only permissions + """ + def on_read_only_error(func, path, exc_info): + os.chmod(path, stat.S_IWRITE) + os.unlink(path) + + # Prevent error if already deleted or never existed, that is our desired state + with suppress(FileNotFoundError): + shutil.rmtree(str(directory), onerror=on_read_only_error if force else None) + + @contextmanager def temporary_working_directory(directory: Path | str) -> Generator[None, None, None]: cwd = os.getcwd() From 2597ea584ab5084ef6ab398b9ec6186a6191ac1f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 13 Jan 2024 10:53:12 -0500 Subject: [PATCH 074/167] style(tests): update typing & import issues --- tests/command_line/test_version.py | 5 +++++ tests/scenario/test_next_version.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index b9d4607ab..19f2a8cc8 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -598,7 +598,11 @@ def test_custom_release_notes_template( ) release_history = get_release_history_from_context(runtime_context_with_no_tags) tag = runtime_context_with_no_tags.repo.tags[-1].name + release_version = runtime_context_with_no_tags.version_translator.from_tag(tag) + if release_version is None: + raise ValueError(f"Could not translate tag '{tag}' to version") + release = release_history.released[release_version] expected_release_notes = ( @@ -614,6 +618,7 @@ def test_custom_release_notes_template( f"'semantic-release {version.name} --skip-build --vcs-release': " + resp.stderr ) assert post_mocker.call_count == 1 + assert post_mocker.last_request is not None assert post_mocker.last_request.json()["body"] == expected_release_notes diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index 3c3907c11..579cf28e9 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -1,7 +1,7 @@ import pytest # Limitation in pytest-lazy-fixture - see https://stackoverflow.com/a/69884019 -from pytest import lazy_fixture +from pytest_lazyfixture import lazy_fixture from semantic_release.version.algorithm import next_version from semantic_release.version.translator import VersionTranslator From 8e3f87ce801498fdbb0c1b2261b9768941c0a21c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 13 Jan 2024 10:58:44 -0500 Subject: [PATCH 075/167] test(fixtures): remove unused fixtures & types --- tests/fixtures/git_repo.py | 43 +++----------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 7ddade570..09c1c9de1 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -19,10 +19,6 @@ VersionStr = str CommitMsg = str - class RepoInitFn(Protocol): - def __call__(self, remote_url: str | None = None) -> Repo: - ... - class ExProjectGitRepoFn(Protocol): def __call__(self) -> Repo: ... @@ -81,9 +77,9 @@ def cached_example_git_project( """ Initializes an example project with git repo. DO NOT USE DIRECTLY. - Use the `git_repo_factory` fixture instead. This creates a default - base repository, all settings can be changed later through the git - repo factory fixture. + Use a `repo_*` fixture instead. This creates a default + base repository, all settings can be changed later through from the + example_project_git_repo fixture's return object and manual adjustment. """ if not cached_example_project.exists(): raise RuntimeError("Unable to find cached project files") @@ -115,39 +111,6 @@ def cached_example_git_project( return teardown_cached_dir(cached_git_proj_path) -@pytest.fixture -def git_repo_factory( - cached_example_git_project: Path, - example_project_dir: ExProjectDir, -) -> Generator[RepoInitFn, None, None]: - repos: list[Repo] = [] - - def git_repo(remote_url: str | None = None) -> Repo: - if not cached_example_git_project.exists(): - raise RuntimeError("Unable to find cached git project files!") - - # Copy the cached git project to the current test's project dir - copy_dir_tree(cached_example_git_project, example_project_dir) - - # Create Git Repo object for project - repo = Repo(example_project_dir) - - # store the repo so we can close it later - repos.append(repo) - - if remote_url is not None: - # update the origin url if desired - repo.remotes.origin.set_url(remote_url) - - return repo - - try: - yield git_repo - finally: - for repo in repos: - repo.close() - - @pytest.fixture def example_project_git_repo(example_project_dir: ExProjectDir) -> Generator[ExProjectGitRepoFn, None, None]: repos: list[Repo] = [] From 16c057a8da471a696f6d6484f2cc821fd883f5ed Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 9 Feb 2024 07:50:40 +0000 Subject: [PATCH 076/167] style: beautify 8e3f87ce801498fdbb0c1b2261b9768941c0a21c --- tests/command_line/test_changelog.py | 7 ++- tests/command_line/test_version.py | 8 +++- tests/fixtures/example_project.py | 15 ++++--- tests/fixtures/git_repo.py | 19 +++++--- .../git_flow/repo_w_2_release_channels.py | 3 +- .../git_flow/repo_w_3_release_channels.py | 3 +- .../github_flow/repo_w_release_channels.py | 15 ++++--- .../repos/trunk_based_dev/repo_w_no_tags.py | 11 +++-- .../trunk_based_dev/repo_w_prereleases.py | 10 ++--- .../repos/trunk_based_dev/repo_w_tags.py | 43 +++++++++++-------- tests/scenario/test_release_history.py | 4 +- tests/util.py | 1 + 12 files changed, 86 insertions(+), 53 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index d6813b365..0f7a4de05 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -41,7 +41,12 @@ lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), "v0.2.0", ), - (lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), "v0.2.0"), + ( + lazy_fixture( + "repo_w_github_flow_w_feature_release_channel_angular_commits" + ), + "v0.2.0", + ), (lazy_fixture("repo_with_git_flow_angular_commits"), "v1.0.0"), ( lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 19f2a8cc8..ba2fbeaed 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -150,7 +150,9 @@ def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_r ], *[ ( - lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), + lazy_fixture( + "repo_w_github_flow_w_feature_release_channel_angular_commits" + ), cli_args, expected_stdout, ) @@ -330,7 +332,9 @@ def test_version_already_released_no_push(repo, cli_runner): ], *[ ( - lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), + lazy_fixture( + "repo_w_github_flow_w_feature_release_channel_angular_commits" + ), cli_args, expected_stdout, ) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index dfb374534..549a33c6f 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -211,21 +211,24 @@ def _use_release_notes_template() -> None: @pytest.fixture def example_pyproject_toml( - example_project_dir: ExProjectDir, pyproject_toml_file: Path, + example_project_dir: ExProjectDir, + pyproject_toml_file: Path, ) -> Path: return example_project_dir / pyproject_toml_file @pytest.fixture def example_setup_cfg( - example_project_dir: ExProjectDir, setup_cfg_file: Path, + example_project_dir: ExProjectDir, + setup_cfg_file: Path, ) -> Path: return example_project_dir / setup_cfg_file @pytest.fixture def example_setup_py( - example_project_dir: ExProjectDir, setup_py_file: Path, + example_project_dir: ExProjectDir, + setup_py_file: Path, ) -> Path: return example_project_dir / setup_py_file @@ -233,14 +236,16 @@ def example_setup_py( # Note this is just the path and the content may change @pytest.fixture def example_changelog_md( - example_project_dir: ExProjectDir, changelog_md_file: Path, + example_project_dir: ExProjectDir, + changelog_md_file: Path, ) -> Path: return example_project_dir / changelog_md_file @pytest.fixture def example_project_template_dir( - example_project_dir: ExProjectDir, changelog_template_dir: Path, + example_project_dir: ExProjectDir, + changelog_template_dir: Path, ) -> Path: return example_project_dir / changelog_template_dir diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 09c1c9de1..0f246ee75 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -28,16 +28,18 @@ def __call__(self) -> list[VersionStr]: ... class GetRepoDefinitionFn(Protocol): - def __call__(self, commit_type: CommitConvention = "angular") -> Mapping[VersionStr, list[CommitMsg]]: + def __call__( + self, commit_type: CommitConvention = "angular" + ) -> Mapping[VersionStr, list[CommitMsg]]: ... class BuildRepoFn(Protocol): def __call__( - self, - git_repo_path: Path | str, - commit_type: CommitConvention, - tag_format_str: str | None = None, - ) -> None: + self, + git_repo_path: Path | str, + commit_type: CommitConvention, + tag_format_str: str | None = None, + ) -> None: ... @@ -112,8 +114,11 @@ def cached_example_git_project( @pytest.fixture -def example_project_git_repo(example_project_dir: ExProjectDir) -> Generator[ExProjectGitRepoFn, None, None]: +def example_project_git_repo( + example_project_dir: ExProjectDir, +) -> Generator[ExProjectGitRepoFn, None, None]: repos: list[Repo] = [] + # Must be a callable function to ensure files exist before repo is opened def _example_project_git_repo() -> Repo: if not example_project_dir.exists(): diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index beb3e37cf..f158e259d 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -174,7 +174,8 @@ def _build_git_flow_repo_with_2_release_channels( raise ValueError(f"Unknown commit type: {commit_type}") git_repo.git.commit( - a=True, m=repo_definition[next_version][0] # Initial commit + a=True, + m=repo_definition[next_version][0], # Initial commit ) # Make initial feature release (v0.1.0) diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index c379649c6..42fd1e6c4 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -180,7 +180,8 @@ def _build_git_flow_repo_w_3_release_channels( raise ValueError(f"Unknown commit type: {commit_type}") git_repo.git.commit( - a=True, m=repo_definition[next_version][0] # Initial commit + a=True, + m=repo_definition[next_version][0], # Initial commit ) # Make initial feature release (v0.1.0) diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index fb738acf8..d4be6f23c 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -87,10 +87,10 @@ def _get_commits_for_github_flow_repo_w_feature_release_channel( def get_versions_for_github_flow_repo_w_feature_release_channel( get_commits_for_github_flow_repo_w_feature_release_channel: GetRepoDefinitionFn, ) -> GetVersionStringsFn: - def _get_versions_for_github_flow_repo_w_feature_release_channel() -> list[VersionStr]: - return list( - get_commits_for_github_flow_repo_w_feature_release_channel().keys() - ) + def _get_versions_for_github_flow_repo_w_feature_release_channel() -> ( + list[VersionStr] + ): + return list(get_commits_for_github_flow_repo_w_feature_release_channel().keys()) return _get_versions_for_github_flow_repo_w_feature_release_channel @@ -112,7 +112,9 @@ def _build_github_flow_repo_w_feature_release_channel( commit_type: CommitConvention, tag_format_str: str | None = None, ) -> None: - repo_definition = get_commits_for_github_flow_repo_w_feature_release_channel(commit_type) + repo_definition = get_commits_for_github_flow_repo_w_feature_release_channel( + commit_type + ) tag_format = tag_format_str or default_tag_format_str versions = list(repo_definition.keys()) next_version = versions[0] @@ -140,7 +142,8 @@ def _build_github_flow_repo_w_feature_release_channel( raise ValueError(f"Unknown commit type: {commit_type}") git_repo.git.commit( - a=True, m=repo_definition[next_version][0] # Initial commit + a=True, + m=repo_definition[next_version][0], # Initial commit ) # Make initial feature release (v0.1.0) diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 0e41ebe52..dbbb2071d 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -51,7 +51,7 @@ def get_commits_for_trunk_only_repo_w_no_tags() -> GetRepoDefinitionFn: "emoji": ":bug: more text", "scipy": "MAINT: more text", "tag": ":nut_and_bolt: more text", - } + }, ], } @@ -73,9 +73,7 @@ def get_versions_for_trunk_only_repo_w_no_tags( get_commits_for_trunk_only_repo_w_no_tags: GetRepoDefinitionFn, ) -> GetVersionStringsFn: def _get_versions_for_trunk_only_repo_w_no_tags() -> list[VersionStr]: - return list( - get_commits_for_trunk_only_repo_w_no_tags().keys() - ) + return list(get_commits_for_trunk_only_repo_w_no_tags().keys()) return _get_versions_for_trunk_only_repo_w_no_tags @@ -116,7 +114,9 @@ def _build_trunk_only_repo_w_no_tags( else: raise ValueError(f"Unknown commit type: {commit_type}") - git_repo.git.commit(a=True, m=repo_definition[next_version][0]) # Initial commit + git_repo.git.commit( + a=True, m=repo_definition[next_version][0] + ) # Initial commit for commit_msg in repo_definition[next_version][1:]: add_text_to_file(git_repo, file_in_repo) @@ -125,7 +125,6 @@ def _build_trunk_only_repo_w_no_tags( return _build_trunk_only_repo_w_no_tags - # --------------------------------------------------------------------------- # # Session-level fixtures to use to set up cached repositories on first use # # --------------------------------------------------------------------------- # diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index 165fd4809..b85a913b6 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -80,9 +80,7 @@ def get_versions_for_trunk_only_repo_w_prerelease_tags( get_commits_for_trunk_only_repo_w_prerelease_tags: GetRepoDefinitionFn, ) -> GetVersionStringsFn: def _get_versions_for_trunk_only_repo_w_prerelease_tags() -> list[VersionStr]: - return list( - get_commits_for_trunk_only_repo_w_prerelease_tags().keys() - ) + return list(get_commits_for_trunk_only_repo_w_prerelease_tags().keys()) return _get_versions_for_trunk_only_repo_w_prerelease_tags @@ -267,7 +265,8 @@ def repo_with_single_branch_and_prereleases_emoji_commits( if not cached_repo_with_single_branch_and_prereleases_emoji_commits.exists(): raise RuntimeError("Unable to find cached repository!") copy_dir_tree( - cached_repo_with_single_branch_and_prereleases_emoji_commits, example_project_dir + cached_repo_with_single_branch_and_prereleases_emoji_commits, + example_project_dir, ) return example_project_git_repo() @@ -282,7 +281,8 @@ def repo_with_single_branch_and_prereleases_scipy_commits( if not cached_repo_with_single_branch_and_prereleases_scipy_commits.exists(): raise RuntimeError("Unable to find cached repository!") copy_dir_tree( - cached_repo_with_single_branch_and_prereleases_scipy_commits, example_project_dir + cached_repo_with_single_branch_and_prereleases_scipy_commits, + example_project_dir, ) return example_project_git_repo() diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index 4a60731df..c47d3ef9c 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -28,21 +28,26 @@ @pytest.fixture(scope="session") def get_commits_for_trunk_only_repo_w_tags() -> GetRepoDefinitionFn: base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { - "0.1.0": [{ - "angular": "Initial commit", - "emoji": "Initial commit", - "scipy": "Initial commit", - "tag": "Initial commit", - }], - "0.1.1": [{ - "angular": "fix: add some more text", - "emoji": ":bug: add some more text", - "scipy": "MAINT: add some more text", - "tag": ":nut_and_bolt: add some more text", - }], + "0.1.0": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + } + ], + "0.1.1": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + } + ], } + def _get_commits_for_trunk_only_repo_w_tags( - commit_type: CommitConvention = "angular" + commit_type: CommitConvention = "angular", ) -> Mapping[VersionStr, list[CommitMsg]]: definition: Mapping[VersionStr, list[CommitMsg]] = {} for version, commits in base_definition.items(): @@ -59,9 +64,7 @@ def get_versions_for_trunk_only_repo_w_tags( get_commits_for_trunk_only_repo_w_tags: GetRepoDefinitionFn, ) -> GetVersionStringsFn: def _get_versions_for_trunk_only_repo_w_tags() -> list[VersionStr]: - return list( - get_commits_for_trunk_only_repo_w_tags().keys() - ) + return list(get_commits_for_trunk_only_repo_w_tags().keys()) return _get_versions_for_trunk_only_repo_w_tags @@ -104,7 +107,9 @@ def _build_trunk_only_repo_w_tags( else: raise ValueError(f"Unknown commit type: {commit_type}") - git_repo.git.commit(a=True, m=repo_definition[next_version][0]) # Initial commit + git_repo.git.commit( + a=True, m=repo_definition[next_version][0] + ) # Initial commit add_text_to_file(git_repo, file_in_repo) git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) @@ -114,7 +119,9 @@ def _build_trunk_only_repo_w_tags( next_version = versions[1] add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) # patch level message + git_repo.git.commit( + m=repo_definition[next_version][0] + ) # patch level message add_text_to_file(git_repo, file_in_repo) git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) diff --git a/tests/scenario/test_release_history.py b/tests/scenario/test_release_history.py index 3cd1b4d04..f71cb7396 100644 --- a/tests/scenario/test_release_history.py +++ b/tests/scenario/test_release_history.py @@ -199,7 +199,9 @@ class FakeReleaseHistoryElements(NamedTuple): REPO_WITH_SINGLE_BRANCH_AND_PRERELEASES_EXPECTED_RELEASE_HISTORY, ), ( - lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), + lazy_fixture( + "repo_w_github_flow_w_feature_release_channel_angular_commits" + ), REPO_WITH_MAIN_AND_FEATURE_BRANCHES_EXPECTED_RELEASE_HISTORY, ), ( diff --git a/tests/util.py b/tests/util.py index 9415cdc1c..871c8d4d1 100644 --- a/tests/util.py +++ b/tests/util.py @@ -55,6 +55,7 @@ def remove_dir_tree(directory: Path | str = ".", force: bool = False) -> None: Helpful for deleting directories with .git/* files, which usually have some read-only permissions """ + def on_read_only_error(func, path, exc_info): os.chmod(path, stat.S_IWRITE) os.unlink(path) From 310af1150129e9d6ef833dd9a18860119b15ee01 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 1 Jan 2024 18:27:06 -0500 Subject: [PATCH 077/167] test(constants): set starting example project version to 0.0.0 --- tests/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/const.py b/tests/const.py index 1810bb7b2..115cb5c4e 100644 --- a/tests/const.py +++ b/tests/const.py @@ -123,7 +123,7 @@ ) EXAMPLE_PROJECT_NAME = "example" -EXAMPLE_PROJECT_VERSION = "0.2.2" +EXAMPLE_PROJECT_VERSION = "0.0.0" # Uses the internal defaults of semantic-release unless otherwise needed for testing # modify the pyproject toml as necessary for the test using update_pyproject_toml() From 1a15761c32de93b03628be0032f6a57f196d6ee1 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 1 Jan 2024 18:22:33 -0500 Subject: [PATCH 078/167] test(fixtures): use const to set example project domain --- tests/const.py | 1 + tests/fixtures/git_repo.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/const.py b/tests/const.py index 115cb5c4e..d53ddd967 100644 --- a/tests/const.py +++ b/tests/const.py @@ -4,6 +4,7 @@ EXAMPLE_REPO_OWNER = "example_owner" EXAMPLE_REPO_NAME = "example_repo" +EXAMPLE_HVCS_DOMAIN = "example.com" COMMIT_MESSAGE = "{version}\n\nAutomatically generated by python-semantic-release\n" diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 0f246ee75..a682b7737 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -5,7 +5,7 @@ import pytest from git import Actor, Repo -from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER +from tests.const import EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER from tests.util import copy_dir_tree, shortuid if TYPE_CHECKING: @@ -60,12 +60,12 @@ def file_in_repo(): @pytest.fixture(scope="session") def example_git_ssh_url(): - return f"git@example.com:{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}.git" + return f"git@{EXAMPLE_HVCS_DOMAIN}:{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}.git" @pytest.fixture(scope="session") def example_git_https_url(): - return f"https://example.com/{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}" + return f"https://{EXAMPLE_HVCS_DOMAIN}/{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}.git" @pytest.fixture(scope="session") From 1303a5a5fcaec326f2cbe625d847c7de821a853d Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 1 Jan 2024 18:23:38 -0500 Subject: [PATCH 079/167] test(fixtures): expand hvcs use fixtures to set remote.domain value --- tests/fixtures/example_project.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 549a33c6f..81151d69b 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -42,7 +42,7 @@ def __call__(self, setting: str, value: Any) -> None: ... class UseHvcsFn(Protocol): - def __call__(self) -> type[HvcsBase]: + def __call__(self, domain: str | None = None) -> type[HvcsBase]: ... class UseParserFn(Protocol): @@ -324,9 +324,10 @@ def _use_tag_parser() -> type[CommitParser]: @pytest.fixture def use_github_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use GitHub as the HVCS.""" - - def _use_github_hvcs() -> type[HvcsBase]: + def _use_github_hvcs(domain: str | None = None) -> type[HvcsBase]: update_pyproject_toml("tool.semantic_release.remote.type", "github") + if domain is not None: + update_pyproject_toml("tool.semantic_release.remote.domain", domain) return Github return _use_github_hvcs @@ -335,9 +336,10 @@ def _use_github_hvcs() -> type[HvcsBase]: @pytest.fixture def use_gitlab_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use GitLab as the HVCS.""" - - def _use_gitlab_hvcs() -> type[HvcsBase]: + def _use_gitlab_hvcs(domain: str | None = None) -> type[HvcsBase]: update_pyproject_toml("tool.semantic_release.remote.type", "gitlab") + if domain is not None: + update_pyproject_toml("tool.semantic_release.remote.domain", domain) return Gitlab return _use_gitlab_hvcs @@ -346,9 +348,10 @@ def _use_gitlab_hvcs() -> type[HvcsBase]: @pytest.fixture def use_gitea_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use Gitea as the HVCS.""" - - def _use_gitea_hvcs() -> type[HvcsBase]: + def _use_gitea_hvcs(domain: str | None = None) -> type[HvcsBase]: update_pyproject_toml("tool.semantic_release.remote.type", "gitea") + if domain is not None: + update_pyproject_toml("tool.semantic_release.remote.domain", domain) return Gitea return _use_gitea_hvcs From 93dc523776af303e0870f42618bb1590948fb826 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 1 Jan 2024 18:31:10 -0500 Subject: [PATCH 080/167] test(fixtures): add manual release commit creation fixture --- tests/fixtures/git_repo.py | 49 +++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index a682b7737..143dd5e4d 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -5,7 +5,7 @@ import pytest from git import Actor, Repo -from tests.const import EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER +from tests.const import COMMIT_MESSAGE, EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER from tests.util import copy_dir_tree, shortuid if TYPE_CHECKING: @@ -13,12 +13,25 @@ from typing import Generator, Literal, Mapping, Protocol from tests.conftest import TeardownCachedDirFn - from tests.fixtures.example_project import ExProjectDir + from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn CommitConvention = Literal["angular", "emoji", "scipy", "tag"] VersionStr = str CommitMsg = str + class BuildRepoFn(Protocol): + def __call__( + self, + git_repo_path: Path | str, + commit_type: CommitConvention, + tag_format_str: str | None = None, + ) -> None: + ... + + class CreateReleaseFn(Protocol): + def __call__(self, git_repo: Repo, version: str, tag_format: str = ...) -> None: + ... + class ExProjectGitRepoFn(Protocol): def __call__(self) -> Repo: ... @@ -33,15 +46,6 @@ def __call__( ) -> Mapping[VersionStr, list[CommitMsg]]: ... - class BuildRepoFn(Protocol): - def __call__( - self, - git_repo_path: Path | str, - commit_type: CommitConvention, - tag_format_str: str | None = None, - ) -> None: - ... - @pytest.fixture(scope="session") def commit_author(): @@ -68,6 +72,29 @@ def example_git_https_url(): return f"https://{EXAMPLE_HVCS_DOMAIN}/{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}.git" +@pytest.fixture(scope="session") +def create_release_tagged_commit( + update_pyproject_toml: UpdatePyprojectTomlFn, + default_tag_format_str: str, +) -> CreateReleaseFn: + def _mimic_semantic_release_commit( + git_repo: Repo, + version: str, + tag_format: str = default_tag_format_str, + ) -> None: + # stamp version into pyproject.toml + update_pyproject_toml("tool.poetry.version", version) + + # commit --all files with version number commit message + git_repo.git.commit(a=True, m=COMMIT_MESSAGE.format(version=version)) + + # tag commit with version number + tag_str = tag_format.format(version=version) + git_repo.git.tag(tag_str, m=tag_str) + + return _mimic_semantic_release_commit + + @pytest.fixture(scope="session") def cached_example_git_project( cached_files_dir: Path, From 29dbc7cf6f9a066d9f6bd785fbeb11e700a586ac Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 1 Jan 2024 18:33:47 -0500 Subject: [PATCH 081/167] test(fixtures): add commit & changelog entry derivation function --- tests/fixtures/git_repo.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 143dd5e4d..39d395ce2 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -12,6 +12,8 @@ from pathlib import Path from typing import Generator, Literal, Mapping, Protocol + from semantic_release.hvcs import HvcsBase + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn @@ -28,6 +30,12 @@ def __call__( ) -> None: ... + class CommitNReturnChangelogEntryFn(Protocol): + def __call__( + self, git_repo: Repo, commit_msg: str, hvcs: HvcsBase + ) -> str: + ... + class CreateReleaseFn(Protocol): def __call__(self, git_repo: Repo, version: str, tag_format: str = ...) -> None: ... @@ -95,6 +103,24 @@ def _mimic_semantic_release_commit( return _mimic_semantic_release_commit +@pytest.fixture(scope="session") +def commit_n_rtn_changelog_entry() -> CommitNReturnChangelogEntryFn: + def _commit_n_rtn_changelog_entry( + git_repo: Repo, commit_msg: str, hvcs: HvcsBase + ) -> str: + # make commit with --all files + git_repo.git.commit(a=True, m=commit_msg) + + # log commit in changelog format after commit action + commit_sha = git_repo.head.commit.hexsha + return str.join(" ", [ + str(git_repo.head.commit.message).strip(), + f"([`{commit_sha[:7]}`]({hvcs.commit_hash_url(commit_sha)}))" + ]) + + return _commit_n_rtn_changelog_entry + + @pytest.fixture(scope="session") def cached_example_git_project( cached_files_dir: Path, From e9605f37b001ff478f08f781587448c16ba31687 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 12:21:16 -0500 Subject: [PATCH 082/167] test(fixtures): add generic multi-commit executor from definition --- tests/fixtures/git_repo.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 39d395ce2..f8d023038 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -6,7 +6,7 @@ from git import Actor, Repo from tests.const import COMMIT_MESSAGE, EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER -from tests.util import copy_dir_tree, shortuid +from tests.util import add_text_to_file, copy_dir_tree, shortuid if TYPE_CHECKING: from pathlib import Path @@ -36,6 +36,12 @@ def __call__( ) -> str: ... + class SimulateChangeCommitsNReturnChangelogEntryFn(Protocol): + def __call__( + self, git_repo: Repo, commit_msgs: list[CommitMsg], hvcs: HvcsBase + ) -> list[CommitMsg]: + ... + class CreateReleaseFn(Protocol): def __call__(self, git_repo: Repo, version: str, tag_format: str = ...) -> None: ... @@ -121,6 +127,25 @@ def _commit_n_rtn_changelog_entry( return _commit_n_rtn_changelog_entry +@pytest.fixture(scope="session") +def simulate_change_commits_n_rtn_changelog_entry( + commit_n_rtn_changelog_entry: CommitNReturnChangelogEntryFn, + file_in_repo: str, +) -> SimulateChangeCommitsNReturnChangelogEntryFn: + def _simulate_change_commits_n_rtn_changelog_entry( + git_repo: Repo, commit_msgs: list[str], hvcs: HvcsBase + ) -> list[str]: + changelog_entries = [] + for commit_msg in commit_msgs: + add_text_to_file(git_repo, file_in_repo) + changelog_entries.append( + commit_n_rtn_changelog_entry(git_repo, commit_msg, hvcs) + ) + return changelog_entries + + return _simulate_change_commits_n_rtn_changelog_entry + + @pytest.fixture(scope="session") def cached_example_git_project( cached_files_dir: Path, From e0e8792d9dcd3b7d3f4a41ef0f8fe2c92a761f55 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 12:24:49 -0500 Subject: [PATCH 083/167] test(fixtures): define new repo definition type --- tests/fixtures/git_repo.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index f8d023038..51bfbf2fc 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from pathlib import Path - from typing import Generator, Literal, Mapping, Protocol + from typing import Generator, Literal, TypedDict, Protocol, Union from semantic_release.hvcs import HvcsBase @@ -20,6 +20,29 @@ CommitConvention = Literal["angular", "emoji", "scipy", "tag"] VersionStr = str CommitMsg = str + ChangelogTypeHeading = str + TomlSerializableTypes = Union[dict, set, list, tuple, int, float, bool, str] + + class RepoVersionDef(TypedDict): + """ + A reduced common repo definition, that is specific to a type of commit conventions + + Used for builder functions that only need to know about a single commit convention type + """ + changelog_sections: list[ChangelogTypeHeadingDef] + commits: list[CommitMsg] + + class ChangelogTypeHeadingDef(TypedDict): + section: ChangelogTypeHeading + i_commits: list[int] + """List of indexes values to match to the commits list in the RepoVersionDef""" + + class BaseRepoVersionDef(TypedDict): + """ + A Common Repo definition for a get_commits_repo_*() fixture with all commit convention types + """ + changelog_sections: dict[CommitConvention, list[ChangelogTypeHeadingDef]] + commits: list[dict[CommitConvention, CommitMsg]] class BuildRepoFn(Protocol): def __call__( @@ -54,6 +77,12 @@ class GetVersionStringsFn(Protocol): def __call__(self) -> list[VersionStr]: ... + RepoDefinition = dict[VersionStr, RepoVersionDef] + """ + A Type alias to define a repositories versions, commits, and changelog sections + for a specific commit convention + """ + class GetRepoDefinitionFn(Protocol): def __call__( self, commit_type: CommitConvention = "angular" From 1e13e2cee25b2760058bdd58238a9f55d5999c5e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 12:35:11 -0500 Subject: [PATCH 084/167] test(fixtures): apply new repo definition to repos --- tests/fixtures/git_repo.py | 4 +- .../git_flow/repo_w_2_release_channels.py | 237 ++++++++++++------ .../git_flow/repo_w_3_release_channels.py | 228 +++++++++++------ .../github_flow/repo_w_release_channels.py | 148 +++++++---- .../repos/trunk_based_dev/repo_w_no_tags.py | 107 +++++--- .../trunk_based_dev/repo_w_prereleases.py | 126 +++++++--- .../repos/trunk_based_dev/repo_w_tags.py | 78 ++++-- 7 files changed, 632 insertions(+), 296 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 51bfbf2fc..d952f6b20 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -84,9 +84,7 @@ def __call__(self) -> list[VersionStr]: """ class GetRepoDefinitionFn(Protocol): - def __call__( - self, commit_type: CommitConvention = "angular" - ) -> Mapping[VersionStr, list[CommitMsg]]: + def __call__(self, commit_type: CommitConvention = "angular") -> RepoDefinition: ... diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index f158e259d..5cf9582cb 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -1,5 +1,6 @@ from __future__ import annotations +from copy import deepcopy from typing import TYPE_CHECKING import pytest @@ -10,104 +11,194 @@ if TYPE_CHECKING: from pathlib import Path - from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn from tests.fixtures.git_repo import ( + BaseRepoVersionDef, BuildRepoFn, CommitConvention, - CommitMsg, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, + RepoDefinition, VersionStr, ) @pytest.fixture(scope="session") def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinitionFn: - base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { - "0.1.0": [ - { - "angular": "Initial commit", - "emoji": "Initial commit", - "scipy": "Initial commit", - "tag": "Initial commit", - } - ], - "0.1.1-rc.1": [ - { - "angular": "fix: add some more text", - "emoji": ":bug: add some more text", - "scipy": "MAINT: add some more text", - "tag": ":nut_and_bolt: add some more text", - } - ], - "1.0.0-rc.1": [ - { - "angular": "feat!: add some more text", - "emoji": ":boom: add some more text", - "scipy": "API: add some more text", - "tag": ":sparkles: add some more text\n\nBREAKING CHANGE: add some more text", - } - ], - "1.0.0": [ - { - "angular": "feat: add some more text", - "emoji": ":sparkles: add some more text", - "scipy": "ENH: add some more text", - "tag": ":sparkles: add some more text", + base_definition: dict[str, BaseRepoVersionDef] = { + "0.1.0": { + "changelog_sections": { + "angular": [{"section": "Unknown", "i_commits": [0]}], + "emoji": [{"section": "Other", "i_commits": [0]}], + "scipy": [{"section": "None", "i_commits": [0]}], + "tag": [{"section": "Unknown", "i_commits": [0]}], }, - ], - "1.1.0": [ - { - "angular": "feat: (dev) add some more text", - "emoji": ":sparkles: (dev) add some more text", - "scipy": "ENH: (dev) add some more text", - "tag": ":sparkles: (dev) add some more text", + "commits": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + } + ], + }, + "0.1.1-rc.1": { + "changelog_sections": { + "angular": [{"section": "Fix", "i_commits": [0]}], + "emoji": [{"section": ":bug:", "i_commits": [0]}], + "scipy": [{"section": "Fix", "i_commits": [0]}], + "tag": [{"section": "Fix", "i_commits": [0]}], }, - ], - "1.1.1": [ - { - "angular": "fix: (dev) add some more text", - "emoji": ":bug: (dev) add some more text", - "scipy": "MAINT: (dev) add some more text", - "tag": ":nut_and_bolt: (dev) add some more text", + "commits": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + } + ], + }, + "1.0.0-rc.1": { + "changelog_sections": { + "angular": [{"section": "Breaking", "i_commits": [0]}], + "emoji": [{"section": ":boom:", "i_commits": [0]}], + "scipy": [{"section": "Breaking", "i_commits": [0]}], + "tag": [{"section": "Breaking", "i_commits": [0]}], }, - ], - "1.2.0-alpha.1": [ - { - "angular": "feat: (feature) add some more text", - "emoji": ":sparkles: (feature) add some more text", - "scipy": "ENH: (feature) add some more text", - "tag": ":sparkles: (feature) add some more text", + "commits": [ + { + "angular": "feat!: add some more text", + "emoji": ":boom: add some more text", + "scipy": "API: add some more text", + "tag": ":sparkles: add some more text\n\nBREAKING CHANGE: add some more text", + } + ], + }, + "1.0.0": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], }, - ], - "1.2.0-alpha.2": [ - { - "angular": "feat: (feature) add some more text", - "emoji": ":sparkles: (feature) add some more text", - "scipy": "ENH: (feature) add some more text", - "tag": ":sparkles: (feature) add some more text", + "commits": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + }, + ], + }, + "1.1.0": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], }, - { - "angular": "fix: (feature) add some missing text", - "emoji": ":bug: (feature) add some missing text", - "scipy": "MAINT: (feature) add some missing text", - "tag": ":nut_and_bolt: (feature) add some missing text", + "commits": [ + { + "angular": "feat: (dev) add some more text", + "emoji": ":sparkles: (dev) add some more text", + "scipy": "ENH: (dev) add some more text", + "tag": ":sparkles: (dev) add some more text", + }, + ], + }, + "1.1.1": { + "changelog_sections": { + "angular": [{"section": "Fix", "i_commits": [0]}], + "emoji": [{"section": ":bug:", "i_commits": [0]}], + "scipy": [{"section": "Fix", "i_commits": [0]}], + "tag": [{"section": "Fix", "i_commits": [0]}], }, - ], + "commits": [ + { + "angular": "fix: (dev) add some more text", + "emoji": ":bug: (dev) add some more text", + "scipy": "MAINT: (dev) add some more text", + "tag": ":nut_and_bolt: (dev) add some more text", + }, + ], + }, + "1.2.0-alpha.1": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + }, + ], + }, + "1.2.0-alpha.2": { + "changelog_sections": { + # ORDER matters here since greater than 1 commit, changelogs sections are alphabetized + # But value is ultimately defined by the commits, which means the commits are + # referenced by index value + "angular": [ + {"section": "Feature", "i_commits": [0]}, + {"section": "Fix", "i_commits": [1]}, + ], + "emoji": [ + {"section": ":bug:", "i_commits": [1]}, + {"section": ":sparkles:", "i_commits": [0]}, + ], + "scipy": [ + {"section": "Feature", "i_commits": [0]}, + {"section": "Fix", "i_commits": [1]}, + ], + "tag": [ + {"section": "Feature", "i_commits": [0]}, + {"section": "Fix", "i_commits": [1]}, + ], + }, + "commits": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + }, + { + "angular": "fix: (feature) add some missing text", + "emoji": ":bug: (feature) add some missing text", + "scipy": "MAINT: (feature) add some missing text", + "tag": ":nut_and_bolt: (feature) add some missing text", + }, + ], + }, } def _get_commits_for_git_flow_repo_with_2_release_channels( commit_type: CommitConvention = "angular", - ) -> Mapping[VersionStr, list[CommitMsg]]: - definition: Mapping[VersionStr, list[CommitMsg]] = {} - for version, commits in base_definition.items(): - definition[version] = [ - message_dict[commit_type] for message_dict in commits - ] + ) -> RepoDefinition: + definition: RepoDefinition = {} + + for version, version_def in base_definition.items(): + definition[version] = { + # Extract the correct changelog section header for the commit type + "changelog_sections": deepcopy( + version_def["changelog_sections"][commit_type] + ), + "commits": [ + # Extract the correct commit message for the commit type + message_variants[commit_type] + for message_variants in version_def["commits"] + ], + } + return definition return _get_commits_for_git_flow_repo_with_2_release_channels diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index 42fd1e6c4..5b75fa3f1 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -1,5 +1,6 @@ from __future__ import annotations +from copy import deepcopy from typing import TYPE_CHECKING import pytest @@ -10,106 +11,189 @@ if TYPE_CHECKING: from pathlib import Path - from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn from tests.fixtures.git_repo import ( + BaseRepoVersionDef, BuildRepoFn, CommitConvention, - CommitMsg, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, + RepoDefinition, VersionStr, ) @pytest.fixture(scope="session") def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: - base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { - "0.1.0": [ - { - "angular": "Initial commit", - "emoji": "Initial commit", - "scipy": "Initial commit", - "tag": "Initial commit", + base_definition: dict[str, BaseRepoVersionDef] = { + "0.1.0": { + "changelog_sections": { + "angular": [{"section": "Unknown", "i_commits": [0]}], + "emoji": [{"section": "Other", "i_commits": [0]}], + "scipy": [{"section": "None", "i_commits": [0]}], + "tag": [{"section": "Unknown", "i_commits": [0]}], }, - ], - "0.1.1-rc.1": [ - { - "angular": "fix: add some more text", - "emoji": ":bug: add some more text", - "scipy": "MAINT: add some more text", - "tag": ":nut_and_bolt: add some more text", + "commits": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + } + ], + }, + "0.1.1-rc.1": { + "changelog_sections": { + "angular": [{"section": "Fix", "i_commits": [0]}], + "emoji": [{"section": ":bug:", "i_commits": [0]}], + "scipy": [{"section": "Fix", "i_commits": [0]}], + "tag": [{"section": "Fix", "i_commits": [0]}], }, - ], - "1.0.0-rc.1": [ - { - "angular": "feat!: add some more text", - "emoji": ":boom: add some more text", - "scipy": "API: add some more text", - "tag": ":sparkles: add some more text\n\nBREAKING CHANGE: add some more text", + "commits": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + } + ], + }, + "1.0.0-rc.1": { + "changelog_sections": { + "angular": [{"section": "Breaking", "i_commits": [0]}], + "emoji": [{"section": ":boom:", "i_commits": [0]}], + "scipy": [{"section": "Breaking", "i_commits": [0]}], + "tag": [{"section": "Breaking", "i_commits": [0]}], }, - ], - "1.0.0": [ - { - "angular": "feat: add some more text", - "emoji": ":sparkles: add some more text", - "scipy": "ENH: add some more text", - "tag": ":sparkles: add some more text", + "commits": [ + { + "angular": "feat!: add some more text", + "emoji": ":boom: add some more text", + "scipy": "API: add some more text", + "tag": ":sparkles: add some more text\n\nBREAKING CHANGE: add some more text", + } + ], + }, + "1.0.0": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], }, - ], - "1.1.0-rc.1": [ - { - "angular": "feat: (dev) add some more text", - "emoji": ":sparkles: (dev) add some more text", - "scipy": "ENH: (dev) add some more text", - "tag": ":sparkles: (dev) add some more text", + "commits": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + }, + ], + }, + "1.1.0-rc.1": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], }, - ], - "1.1.0-rc.2": [ - { - "angular": "fix: (dev) add some more text", - "emoji": ":bug: (dev) add some more text", - "scipy": "MAINT: (dev) add some more text", - "tag": ":nut_and_bolt: (dev) add some more text", + "commits": [ + { + "angular": "feat: (dev) add some more text", + "emoji": ":sparkles: (dev) add some more text", + "scipy": "ENH: (dev) add some more text", + "tag": ":sparkles: (dev) add some more text", + }, + ], + }, + "1.1.0-rc.2": { + "changelog_sections": { + "angular": [{"section": "Fix", "i_commits": [0]}], + "emoji": [{"section": ":bug:", "i_commits": [0]}], + "scipy": [{"section": "Fix", "i_commits": [0]}], + "tag": [{"section": "Fix", "i_commits": [0]}], }, - ], - "1.1.0-alpha.1": [ - { - "angular": "feat: (feature) add some more text", - "emoji": ":sparkles: (feature) add some more text", - "scipy": "ENH: (feature) add some more text", - "tag": ":sparkles: (feature) add some more text", + "commits": [ + { + "angular": "fix: (dev) add some more text", + "emoji": ":bug: (dev) add some more text", + "scipy": "MAINT: (dev) add some more text", + "tag": ":nut_and_bolt: (dev) add some more text", + }, + ], + }, + "1.1.0-alpha.1": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], }, - ], - "1.1.0-alpha.2": [ - { - "angular": "feat: (feature) add some more text", - "emoji": ":sparkles: (feature) add some more text", - "scipy": "ENH: (feature) add some more text", - "tag": ":sparkles: (feature) add some more text", + "commits": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + }, + ], + }, + "1.1.0-alpha.2": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], }, - ], - "1.1.0-alpha.3": [ - { - "angular": "fix: (feature) add some more text", - "emoji": ":bug: (feature) add some more text", - "scipy": "MAINT: (feature) add some more text", - "tag": ":nut_and_bolt: (feature) add some more text", + "commits": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + }, + ], + }, + "1.1.0-alpha.3": { + "changelog_sections": { + "angular": [{"section": "Fix", "i_commits": [0]}], + "emoji": [{"section": ":bug:", "i_commits": [0]}], + "scipy": [{"section": "Fix", "i_commits": [0]}], + "tag": [{"section": "Fix", "i_commits": [0]}], }, - ], + "commits": [ + { + "angular": "fix: (feature) add some more text", + "emoji": ":bug: (feature) add some more text", + "scipy": "MAINT: (feature) add some more text", + "tag": ":nut_and_bolt: (feature) add some more text", + }, + ], + } } def _get_commits_for_git_flow_repo_w_3_release_channels( commit_type: CommitConvention = "angular", - ) -> Mapping[VersionStr, list[CommitMsg]]: - definition: Mapping[VersionStr, list[CommitMsg]] = {} - for version, commits in base_definition.items(): - definition[version] = [ - message_dict[commit_type] for message_dict in commits - ] + ) -> RepoDefinition: + definition: RepoDefinition = {} + + for version, version_def in base_definition.items(): + definition[version] = { + # Extract the correct changelog section header for the commit type + "changelog_sections": deepcopy( + version_def["changelog_sections"][commit_type] + ), + "commits": [ + # Extract the correct commit message for the commit type + message_variants[commit_type] + for message_variants in version_def["commits"] + ], + } + return definition return _get_commits_for_git_flow_repo_w_3_release_channels diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index d4be6f23c..0c1dd8539 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -1,5 +1,6 @@ from __future__ import annotations +from copy import deepcopy from typing import TYPE_CHECKING import pytest @@ -10,74 +11,125 @@ if TYPE_CHECKING: from pathlib import Path - from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn from tests.fixtures.git_repo import ( + BaseRepoVersionDef, BuildRepoFn, CommitConvention, - CommitMsg, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, + RepoDefinition, VersionStr, ) @pytest.fixture(scope="session") def get_commits_for_github_flow_repo_w_feature_release_channel() -> GetRepoDefinitionFn: - base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { - "0.1.0": [ - { - "angular": "Initial commit", - "emoji": "Initial commit", - "scipy": "Initial commit", - "tag": "Initial commit", + base_definition: dict[str, BaseRepoVersionDef] = { + "0.1.0": { + "changelog_sections": { + "angular": [{"section": "Unknown", "i_commits": [0]}], + "emoji": [{"section": "Other", "i_commits": [0]}], + "scipy": [{"section": "None", "i_commits": [0]}], + "tag": [{"section": "Unknown", "i_commits": [0]}], }, - ], - "0.1.1-rc.1": [ - { - "angular": "fix: add some more text", - "emoji": ":bug: add some more text", - "scipy": "MAINT: add some more text", - "tag": ":nut_and_bolt: add some more text", - } - ], - "0.2.0-rc.1": [ - { - "angular": "feat: add some more text", - "emoji": ":sparkles: add some more text", - "scipy": "ENH: add some more text", - "tag": ":sparkles: add some more text", - } - ], - "0.2.0": [ - { - "angular": "feat: add some more text", - "emoji": ":sparkles: add some more text", - "scipy": "ENH: add some more text", - "tag": ":sparkles: add some more text", - } - ], - "0.3.0-beta.1": [ - { - "angular": "feat: (feature) add some more text", - "emoji": ":sparkles: (feature) add some more text", - "scipy": "ENH: (feature) add some more text", - "tag": ":sparkles: (feature) add some more text", - } - ], + "commits": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + } + ], + }, + "0.1.1-rc.1": { + "changelog_sections": { + "angular": [{"section": "Fix", "i_commits": [0]}], + "emoji": [{"section": ":bug:", "i_commits": [0]}], + "scipy": [{"section": "Fix", "i_commits": [0]}], + "tag": [{"section": "Fix", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + } + ], + }, + "0.2.0-rc.1": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + }, + ], + }, + "0.2.0": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + }, + ], + }, + "0.3.0-beta.1": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "feat: (feature) add some more text", + "emoji": ":sparkles: (feature) add some more text", + "scipy": "ENH: (feature) add some more text", + "tag": ":sparkles: (feature) add some more text", + }, + ], + }, } def _get_commits_for_github_flow_repo_w_feature_release_channel( commit_type: CommitConvention = "angular", - ) -> Mapping[VersionStr, list[CommitMsg]]: - definition: Mapping[VersionStr, list[CommitMsg]] = {} - for version, commits in base_definition.items(): - definition[version] = [ - message_dict[commit_type] for message_dict in commits - ] + ) -> RepoDefinition: + definition: RepoDefinition = {} + + for version, version_def in base_definition.items(): + definition[version] = { + # Extract the correct changelog section header for the commit type + "changelog_sections": deepcopy( + version_def["changelog_sections"][commit_type] + ), + "commits": [ + # Extract the correct commit message for the commit type + message_variants[commit_type] + for message_variants in version_def["commits"] + ], + } + return definition return _get_commits_for_github_flow_repo_w_feature_release_channel diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index dbbb2071d..2ad9c1932 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -1,5 +1,6 @@ from __future__ import annotations +from copy import deepcopy from typing import TYPE_CHECKING import pytest @@ -9,60 +10,98 @@ if TYPE_CHECKING: from pathlib import Path - from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import ExProjectDir, UseParserFn from tests.fixtures.git_repo import ( + BaseRepoVersionDef, BuildRepoFn, CommitConvention, - CommitMsg, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, + RepoDefinition, VersionStr, ) @pytest.fixture(scope="session") def get_commits_for_trunk_only_repo_w_no_tags() -> GetRepoDefinitionFn: - base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { - "0.1.0": [ - { - "angular": "Initial commit", - "emoji": "Initial commit", - "scipy": "Initial commit", - "tag": "Initial commit", - }, - { - "angular": "fix: add some more text", - "emoji": ":bug: add some more text", - "scipy": "MAINT: add some more text", - "tag": ":nut_and_bolt: add some more text", - }, - { - "angular": "feat: add much more text", - "emoji": ":sparkles: add much more text", - "scipy": "ENH: add much more text", - "tag": ":sparkles: add much more text", + base_definition: dict[str, BaseRepoVersionDef] = { + "Unreleased": { + "changelog_sections": { + # ORDER matters here since greater than 1 commit, changelogs sections are alphabetized + # But value is ultimately defined by the commits, which means the commits are + # referenced by index value + "angular": [ + {"section": "Feature", "i_commits": [2]}, + {"section": "Fix", "i_commits": [3, 1]}, + {"section": "Unknown", "i_commits": [0]}, + ], + "emoji": [ + {"section": ":bug:", "i_commits": [1, 3]}, + {"section": ":sparkles:", "i_commits": [2]}, + {"section": "Other", "i_commits": [0]}, + ], + "scipy": [ + {"section": "Feature", "i_commits": [2]}, + {"section": "Fix", "i_commits": [1, 3]}, + {"section": "None", "i_commits": [0]}, + ], + "tag": [ + {"section": "Feature", "i_commits": [2]}, + {"section": "Fix", "i_commits": [1, 3]}, + {"section": "Unknown", "i_commits": [0]}, + ], }, - { - "angular": "fix: more text", - "emoji": ":bug: more text", - "scipy": "MAINT: more text", - "tag": ":nut_and_bolt: more text", - }, - ], + "commits": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + }, + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + }, + { + "angular": "feat: add much more text", + "emoji": ":sparkles: add much more text", + "scipy": "ENH: add much more text", + "tag": ":sparkles: add much more text", + }, + { + "angular": "fix: more text", + "emoji": ":bug: more text", + "scipy": "MAINT: more text", + "tag": ":nut_and_bolt: more text", + }, + ], + }, } def _get_commits_for_trunk_only_repo_w_no_tags( commit_type: CommitConvention = "angular", - ) -> Mapping[VersionStr, list[CommitMsg]]: - definition: Mapping[VersionStr, list[CommitMsg]] = {} - for version, commits in base_definition.items(): - definition[version] = [ - message_dict[commit_type] for message_dict in commits - ] + ) -> RepoDefinition: + definition: RepoDefinition = {} + + for version, version_def in base_definition.items(): + definition[version] = { + # Extract the correct changelog section header for the commit type + "changelog_sections": deepcopy( + version_def["changelog_sections"][commit_type] + ), + "commits": [ + # Extract the correct commit message for the commit type + message_variants[commit_type] + for message_variants in version_def["commits"] + ], + } + return definition return _get_commits_for_trunk_only_repo_w_no_tags diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index b85a913b6..7fdb1814c 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -1,5 +1,6 @@ from __future__ import annotations +from copy import deepcopy from typing import TYPE_CHECKING import pytest @@ -10,66 +11,109 @@ if TYPE_CHECKING: from pathlib import Path - from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UseParserFn from tests.fixtures.git_repo import ( + BaseRepoVersionDef, BuildRepoFn, CommitConvention, - CommitMsg, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, + RepoDefinition, VersionStr, ) @pytest.fixture(scope="session") def get_commits_for_trunk_only_repo_w_prerelease_tags() -> GetRepoDefinitionFn: - base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { - "0.1.0": [ - { - "angular": "Initial commit", - "emoji": "Initial commit", - "scipy": "Initial commit", - "tag": "Initial commit", - } - ], - "0.1.1-rc.1": [ - { - "angular": "fix: add some more text", - "emoji": ":bug: add some more text", - "scipy": "MAINT: add some more text", - "tag": ":nut_and_bolt: add some more text", - } - ], - "0.2.0-rc.1": [ - { - "angular": "feat: add some more text", - "emoji": ":sparkles: add some more text", - "scipy": "ENH: add some more text", - "tag": ":sparkles: add some more text", - } - ], - "0.2.0": [ - { - "angular": "feat: add some more text", - "emoji": ":sparkles: add some more text", - "scipy": "ENH: add some more text", - "tag": ":sparkles: add some more text", - } - ], + base_definition: dict[str, BaseRepoVersionDef] = { + "0.1.0": { + "changelog_sections": { + "angular": [{"section": "Unknown", "i_commits": [0]}], + "emoji": [{"section": "Other", "i_commits": [0]}], + "scipy": [{"section": "None", "i_commits": [0]}], + "tag": [{"section": "Unknown", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + } + ], + }, + "0.1.1-rc.1": { + "changelog_sections": { + "angular": [{"section": "Fix", "i_commits": [0]}], + "emoji": [{"section": ":bug:", "i_commits": [0]}], + "scipy": [{"section": "Fix", "i_commits": [0]}], + "tag": [{"section": "Fix", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + } + ], + }, + "0.2.0-rc.1": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + }, + ], + }, + "0.2.0": { + "changelog_sections": { + "angular": [{"section": "Feature", "i_commits": [0]}], + "emoji": [{"section": ":sparkles:", "i_commits": [0]}], + "scipy": [{"section": "Feature", "i_commits": [0]}], + "tag": [{"section": "Feature", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "feat: add some more text", + "emoji": ":sparkles: add some more text", + "scipy": "ENH: add some more text", + "tag": ":sparkles: add some more text", + }, + ], + }, } def _get_commits_for_trunk_only_repo_w_prerelease_tags( commit_type: CommitConvention = "angular", - ) -> Mapping[VersionStr, list[CommitMsg]]: - definition: Mapping[VersionStr, list[CommitMsg]] = {} - for version, commits in base_definition.items(): - definition[version] = [ - message_dict[commit_type] for message_dict in commits - ] + ) -> RepoDefinition: + definition: RepoDefinition = {} + + for version, version_def in base_definition.items(): + definition[version] = { + # Extract the correct changelog section header for the commit type + "changelog_sections": deepcopy( + version_def["changelog_sections"][commit_type] + ), + "commits": [ + # Extract the correct commit message for the commit type + message_variants[commit_type] + for message_variants in version_def["commits"] + ], + } + return definition return _get_commits_for_trunk_only_repo_w_prerelease_tags diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index c47d3ef9c..da11a0ea8 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -1,5 +1,6 @@ from __future__ import annotations +from copy import deepcopy from typing import TYPE_CHECKING import pytest @@ -10,50 +11,77 @@ if TYPE_CHECKING: from pathlib import Path - from typing import Mapping + from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import UseParserFn from tests.fixtures.git_repo import ( + BaseRepoVersionDef, BuildRepoFn, CommitConvention, - CommitMsg, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, + RepoDefinition, VersionStr, ) @pytest.fixture(scope="session") def get_commits_for_trunk_only_repo_w_tags() -> GetRepoDefinitionFn: - base_definition: Mapping[VersionStr, list[dict[CommitConvention, CommitMsg]]] = { - "0.1.0": [ - { - "angular": "Initial commit", - "emoji": "Initial commit", - "scipy": "Initial commit", - "tag": "Initial commit", - } - ], - "0.1.1": [ - { - "angular": "fix: add some more text", - "emoji": ":bug: add some more text", - "scipy": "MAINT: add some more text", - "tag": ":nut_and_bolt: add some more text", - } - ], + base_definition: dict[str, BaseRepoVersionDef] = { + "0.1.0": { + "changelog_sections": { + "angular": [{"section": "Unknown", "i_commits": [0]}], + "emoji": [{"section": "Other", "i_commits": [0]}], + "scipy": [{"section": "None", "i_commits": [0]}], + "tag": [{"section": "Unknown", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "Initial commit", + "emoji": "Initial commit", + "scipy": "Initial commit", + "tag": "Initial commit", + } + ], + }, + "0.1.1": { + "changelog_sections": { + "angular": [{"section": "Fix", "i_commits": [0]}], + "emoji": [{"section": ":bug:", "i_commits": [0]}], + "scipy": [{"section": "Fix", "i_commits": [0]}], + "tag": [{"section": "Fix", "i_commits": [0]}], + }, + "commits": [ + { + "angular": "fix: add some more text", + "emoji": ":bug: add some more text", + "scipy": "MAINT: add some more text", + "tag": ":nut_and_bolt: add some more text", + } + ], + }, } def _get_commits_for_trunk_only_repo_w_tags( commit_type: CommitConvention = "angular", - ) -> Mapping[VersionStr, list[CommitMsg]]: - definition: Mapping[VersionStr, list[CommitMsg]] = {} - for version, commits in base_definition.items(): - definition[version] = [ - message_dict[commit_type] for message_dict in commits - ] + ) -> RepoDefinition: + definition: RepoDefinition = {} + + for version, version_def in base_definition.items(): + definition[version] = { + # Extract the correct changelog section header for the commit type + "changelog_sections": deepcopy( + version_def["changelog_sections"][commit_type] + ), + "commits": [ + # Extract the correct commit message for the commit type + message_variants[commit_type] + for message_variants in version_def["commits"] + ], + } + return definition return _get_commits_for_trunk_only_repo_w_tags From 8da284095173f7bcece768e0452d8d18378454d4 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 12:44:27 -0500 Subject: [PATCH 085/167] test(fixtures): add common repo build/setup fixture --- tests/fixtures/example_project.py | 6 +- tests/fixtures/git_repo.py | 93 +++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 81151d69b..ebc5b2694 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -321,7 +321,7 @@ def _use_tag_parser() -> type[CommitParser]: return _use_tag_parser -@pytest.fixture +@pytest.fixture(scope="session") def use_github_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use GitHub as the HVCS.""" def _use_github_hvcs(domain: str | None = None) -> type[HvcsBase]: @@ -333,7 +333,7 @@ def _use_github_hvcs(domain: str | None = None) -> type[HvcsBase]: return _use_github_hvcs -@pytest.fixture +@pytest.fixture(scope="session") def use_gitlab_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use GitLab as the HVCS.""" def _use_gitlab_hvcs(domain: str | None = None) -> type[HvcsBase]: @@ -345,7 +345,7 @@ def _use_gitlab_hvcs(domain: str | None = None) -> type[HvcsBase]: return _use_gitlab_hvcs -@pytest.fixture +@pytest.fixture(scope="session") def use_gitea_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use Gitea as the HVCS.""" def _use_gitea_hvcs(domain: str | None = None) -> type[HvcsBase]: diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index d952f6b20..41af3c2d5 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -1,21 +1,23 @@ from __future__ import annotations +from pathlib import Path from typing import TYPE_CHECKING import pytest from git import Actor, Repo from tests.const import COMMIT_MESSAGE, EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER -from tests.util import add_text_to_file, copy_dir_tree, shortuid +from tests.util import add_text_to_file, copy_dir_tree, shortuid, temporary_working_directory if TYPE_CHECKING: - from pathlib import Path from typing import Generator, Literal, TypedDict, Protocol, Union from semantic_release.hvcs import HvcsBase from tests.conftest import TeardownCachedDirFn - from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn + from tests.fixtures.example_project import ( + ExProjectDir, UpdatePyprojectTomlFn, UseHvcsFn, UseParserFn + ) CommitConvention = Literal["angular", "emoji", "scipy", "tag"] VersionStr = str @@ -47,10 +49,13 @@ class BaseRepoVersionDef(TypedDict): class BuildRepoFn(Protocol): def __call__( self, - git_repo_path: Path | str, - commit_type: CommitConvention, + dest_dir: Path | str, + commit_type: CommitConvention = ..., + hvcs_client_name: str = ..., + hvcs_domain: str = ..., tag_format_str: str | None = None, - ) -> None: + extra_configs: dict[str, TomlSerializableTypes] | None = None, + ) -> tuple[Path, HvcsBase]: ... class CommitNReturnChangelogEntryFn(Protocol): @@ -218,6 +223,82 @@ def cached_example_git_project( return teardown_cached_dir(cached_git_proj_path) +@pytest.fixture(scope="session") +def build_configured_base_repo( + cached_example_git_project: Path, + use_github_hvcs: UseHvcsFn, + use_gitlab_hvcs: UseHvcsFn, + use_gitea_hvcs: UseHvcsFn, + use_angular_parser: UseParserFn, + use_emoji_parser: UseParserFn, + use_scipy_parser: UseParserFn, + use_tag_parser: UseParserFn, + example_git_https_url: str, + update_pyproject_toml: UpdatePyprojectTomlFn, +) -> BuildRepoFn: + """ + This fixture is intended to simplify repo scenario building by initially + creating the repo but also configuring semantic_release in the pyproject.toml + for when the test executes semantic_release. It returns a function so that + derivative fixtures can call this fixture with individual parameters. + """ + def _build_configured_base_repo( + dest_dir: Path | str, + commit_type: str = "angular", + hvcs_client_name: str = "github", + hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, + tag_format_str: str | None = None, + extra_configs: dict[str, TomlSerializableTypes] | None = None, + ) -> tuple[Path, HvcsBase]: + if not cached_example_git_project.exists(): + raise RuntimeError(f"Unable to find cached git project files!") + + # Copy the cached git project the dest directory + copy_dir_tree(cached_example_git_project, dest_dir) + + # Make sure we are in the dest directory + with temporary_working_directory(dest_dir): + + # Set parser configuration + if commit_type == "angular": + use_angular_parser() + elif commit_type == "emoji": + use_emoji_parser() + elif commit_type == "scipy": + use_scipy_parser() + elif commit_type == "tag": + use_tag_parser() + else: + raise ValueError(f"Unknown parser name: {commit_type}") + + # Set HVCS configuration + if hvcs_client_name == "github": + hvcs_class = use_github_hvcs(hvcs_domain) + elif hvcs_client_name == "gitlab": + hvcs_class = use_gitlab_hvcs(hvcs_domain) + elif hvcs_client_name == "gitea": + hvcs_class = use_gitea_hvcs(hvcs_domain) + else: + raise ValueError(f"Unknown HVCS client name: {hvcs_client_name}") + + # Create HVCS Client instance + hvcs = hvcs_class(example_git_https_url, hvcs_domain) + + # Set tag format in configuration + if tag_format_str is not None: + update_pyproject_toml("tool.semantic_release.tag_format", tag_format_str) + + # Apply configurations to pyproject.toml + if extra_configs is not None: + for key, value in extra_configs.items(): + update_pyproject_toml(key, value) + + return Path(dest_dir), hvcs + + return _build_configured_base_repo + + + @pytest.fixture def example_project_git_repo( example_project_dir: ExProjectDir, From 9ad829693589e0cfe0962159f41cc2efd886eacc Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 12:45:14 -0500 Subject: [PATCH 086/167] test(fixtures): refactor repos to use common fixtures to simplify workflow --- .../git_flow/repo_w_2_release_channels.py | 223 +++++++++--------- .../git_flow/repo_w_3_release_channels.py | 218 +++++++++-------- .../github_flow/repo_w_release_channels.py | 146 ++++++------ .../repos/trunk_based_dev/repo_w_no_tags.py | 74 +++--- .../trunk_based_dev/repo_w_prereleases.py | 167 +++++++------ .../repos/trunk_based_dev/repo_w_tags.py | 118 ++++----- 6 files changed, 470 insertions(+), 476 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 5cf9582cb..29acb7c9e 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -6,23 +6,27 @@ import pytest from git import Repo -from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory +from tests.const import EXAMPLE_HVCS_DOMAIN +from tests.util import copy_dir_tree, temporary_working_directory if TYPE_CHECKING: from pathlib import Path + from semantic_release.hvcs import HvcsBase from tests.conftest import TeardownCachedDirFn - from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn + from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( BaseRepoVersionDef, BuildRepoFn, CommitConvention, + CreateReleaseFn, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, RepoDefinition, + SimulateChangeCommitsNReturnChangelogEntryFn, + TomlSerializableTypes, VersionStr, ) @@ -217,164 +221,167 @@ def _get_versions_for_git_flow_repo_with_2_release_channels() -> list[VersionStr @pytest.fixture(scope="session") def build_git_flow_repo_with_2_release_channels( get_commits_for_git_flow_repo_with_2_release_channels: GetRepoDefinitionFn, - cached_example_git_project: Path, - update_pyproject_toml: UpdatePyprojectTomlFn, - use_angular_parser: UseParserFn, - use_emoji_parser: UseParserFn, - use_scipy_parser: UseParserFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: + """ + This fixture returns a function that when called will build a git repo that + uses the git flow branching strategy with 2 release channels + 1. alpha feature releases + 2. release candidate releases + """ + def _build_git_flow_repo_with_2_release_channels( - git_repo_path: Path | str, - commit_type: CommitConvention, + dest_dir: Path | str, + commit_type: CommitConvention = "angular", + hvcs_client_name: str = "github", + hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, - ) -> None: - repo_definition = get_commits_for_git_flow_repo_with_2_release_channels( - commit_type - ) - tag_format = tag_format_str or default_tag_format_str - versions = list(repo_definition.keys()) - next_version = versions[0] - - if not cached_example_git_project.exists(): - raise RuntimeError("Unable to find cached example git project!") - - copy_dir_tree(cached_example_git_project, git_repo_path) - - with temporary_working_directory(git_repo_path), Repo(".") as git_repo: - update_pyproject_toml( - "tool.semantic_release.branches.features", - { + extra_configs: dict[str, TomlSerializableTypes] | None = None, + ) -> tuple[Path, HvcsBase]: + repo_dir, hvcs = build_configured_base_repo( + dest_dir, + commit_type=commit_type, + hvcs_client_name=hvcs_client_name, + hvcs_domain=hvcs_domain, + tag_format_str=tag_format_str, + extra_configs={ + # branch "feature" has prerelease suffix of "alpha" + "tool.semantic_release.branches.features": { "match": "feat.*", "prerelease": True, "prerelease_token": "alpha", }, - ) + **(extra_configs or {}), + }, + ) + + # Retrieve/Define project vars that will be used to create the repo below + repo_def = get_commits_for_git_flow_repo_with_2_release_channels(commit_type) + versions = (key for key in repo_def) + next_version = next(versions) + next_version_def = repo_def[next_version] - if commit_type == "angular": - use_angular_parser() - elif commit_type == "emoji": - use_emoji_parser() - elif commit_type == "scipy": - use_scipy_parser() - elif commit_type == "tag": - use_tag_parser() - else: - raise ValueError(f"Unknown commit type: {commit_type}") - - git_repo.git.commit( - a=True, - m=repo_definition[next_version][0], # Initial commit + # must be after build_configured_base_repo() so we dont set the + # default tag format in the pyproject.toml (we want semantic-release to use its defaults) + # however we need it to manually create the tags it knows how to parse + tag_format = tag_format_str or default_tag_format_str + + # Run Git operations to simulate repo commit & release history + with temporary_working_directory(repo_dir), Repo(".") as git_repo: + # commit initial files & update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs ) - # Make initial feature release (v0.1.0) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + # Publish initial feature release (v0.1.0) [updates tool.poetry.version] + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[1] + next_version = next(versions) + next_version_def = repo_def[next_version] - # Prepare for a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + # Prepare to do a prerelease (by adding a change) + # modify && commit modification -> update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a patch level release candidate (v0.1.1-rc.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[2] + next_version = next(versions) + next_version_def = repo_def[next_version] # Prepare for a major feature release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + # modify && commit modification -> update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a major feature release candidate (v1.0.0-rc.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[3] + next_version = next(versions) + next_version_def = repo_def[next_version] # Prepare for a major feature release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + # modify && commit modification -> update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a major feature release (v1.0.0) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[4] + next_version = next(versions) + next_version_def = repo_def[next_version] # Change to a dev branch git_repo.create_head("dev") git_repo.heads.dev.checkout() + # TODO: FIX this section... its not proper Git Flow + # Prepare for a minor feature release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + # modify && commit modification -> update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) + # TODO: ERROR releasing on dev branch # Make a minor feature release (v1.1.0) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[5] + next_version = next(versions) + next_version_def = repo_def[next_version] # Prepare for a patch level release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + # modify && commit modification -> update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) + # TODO: ERROR releasing on dev branch # Make a patch level release (v1.1.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[6] + next_version = next(versions) + next_version_def = repo_def[next_version] # Change to a feature branch git_repo.create_head("feature") git_repo.heads.feature.checkout() - # Prepare for a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + # Prepare for an alpha prerelease + # modify && commit modification -> update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) - # Make an alpha prerelease (v1.2.0-alpha.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + # Make an alpha prerelease (v1.2.0-alpha.1) on the feature branch + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[7] + next_version = next(versions) + next_version_def = repo_def[next_version] # Prepare for a 2nd prerelease with 2 commits - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][1]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) + + # Make a 2nd alpha prerelease (v1.2.0-alpha.2) on the feature branch + create_release_tagged_commit(git_repo, next_version, tag_format) - # Make a 2nd alpha prerelease (v1.2.0-alpha.2) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + return repo_dir, hvcs return _build_git_flow_repo_with_2_release_channels @@ -445,7 +452,7 @@ def cached_repo_w_git_flow_n_2_release_channels_tag_commits( def repo_with_git_flow_angular_commits( cached_repo_w_git_flow_n_2_release_channels_angular_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_git_flow_n_2_release_channels_angular_commits.exists(): @@ -461,7 +468,7 @@ def repo_with_git_flow_angular_commits( def repo_with_git_flow_emoji_commits( cached_repo_w_git_flow_n_2_release_channels_emoji_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_git_flow_n_2_release_channels_emoji_commits.exists(): @@ -477,7 +484,7 @@ def repo_with_git_flow_emoji_commits( def repo_with_git_flow_scipy_commits( cached_repo_w_git_flow_n_2_release_channels_scipy_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_git_flow_n_2_release_channels_scipy_commits.exists(): @@ -493,7 +500,7 @@ def repo_with_git_flow_scipy_commits( def repo_with_git_flow_tag_commits( cached_repo_w_git_flow_n_2_release_channels_tag_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_git_flow_n_2_release_channels_tag_commits.exists(): diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index 5b75fa3f1..b6929b652 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -6,23 +6,27 @@ import pytest from git import Repo -from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory +from tests.const import EXAMPLE_HVCS_DOMAIN +from tests.util import copy_dir_tree, temporary_working_directory if TYPE_CHECKING: from pathlib import Path + from semantic_release.hvcs import HvcsBase from tests.conftest import TeardownCachedDirFn - from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn + from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( BaseRepoVersionDef, BuildRepoFn, CommitConvention, + CreateReleaseFn, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, RepoDefinition, + SimulateChangeCommitsNReturnChangelogEntryFn, + TomlSerializableTypes, VersionStr, ) @@ -212,179 +216,167 @@ def _get_versions_for_git_flow_repo_w_3_release_channels() -> list[VersionStr]: @pytest.fixture(scope="session") def build_git_flow_repo_w_3_release_channels( get_commits_for_git_flow_repo_w_3_release_channels: GetRepoDefinitionFn, - cached_example_git_project: Path, - update_pyproject_toml: UpdatePyprojectTomlFn, - use_angular_parser: UseParserFn, - use_emoji_parser: UseParserFn, - use_scipy_parser: UseParserFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_git_flow_repo_w_3_release_channels( - git_repo_path: Path | str, - commit_type: CommitConvention, + dest_dir: Path | str, + commit_type: CommitConvention = "angular", + hvcs_client_name: str = "github", + hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, - ) -> None: - repo_definition = get_commits_for_git_flow_repo_w_3_release_channels( - commit_type - ) - tag_format = tag_format_str or default_tag_format_str - versions = list(repo_definition.keys()) - next_version = versions[0] - - if not cached_example_git_project.exists(): - raise RuntimeError("Unable to find cached example git project!") - - copy_dir_tree(cached_example_git_project, git_repo_path) - - with temporary_working_directory(git_repo_path), Repo(".") as git_repo: - update_pyproject_toml( - "tool.semantic_release.branches.dev", - {"match": "dev", "prerelease": True, "prerelease_token": "rc"}, - ) - update_pyproject_toml( - "tool.semantic_release.branches.features", - { + extra_configs: dict[str, TomlSerializableTypes] | None = None, + ) -> tuple[Path, HvcsBase]: + repo_dir, hvcs = build_configured_base_repo( + dest_dir, + commit_type=commit_type, + hvcs_client_name=hvcs_client_name, + hvcs_domain=hvcs_domain, + tag_format_str=tag_format_str, + extra_configs={ + # branch "dev" has prerelease suffix of "rc" + "tool.semantic_release.branches.dev": { + "match": "dev", + "prerelease": True, + "prerelease_token": "rc" + }, + # branch "feature" has prerelease suffix of "alpha" + "tool.semantic_release.branches.features": { "match": "feat.*", "prerelease": True, "prerelease_token": "alpha", }, - ) + **(extra_configs or {}), + }, + ) - if commit_type == "angular": - use_angular_parser() - elif commit_type == "emoji": - use_emoji_parser() - elif commit_type == "scipy": - use_scipy_parser() - elif commit_type == "tag": - use_tag_parser() - else: - raise ValueError(f"Unknown commit type: {commit_type}") - - git_repo.git.commit( - a=True, - m=repo_definition[next_version][0], # Initial commit + # Retrieve/Define project vars that will be used to create the repo below + repo_def = get_commits_for_git_flow_repo_w_3_release_channels(commit_type) + versions = (key for key in repo_def) + next_version = next(versions) + next_version_def = repo_def[next_version] + + # must be after build_configured_base_repo() so we dont set the + # default tag format in the pyproject.toml (we want semantic-release to use its defaults) + # however we need it to manually create the tags it knows how to parse + tag_format = tag_format_str or default_tag_format_str + + with temporary_working_directory(repo_dir), Repo(".") as git_repo: + # commit initial files & update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs ) # Make initial feature release (v0.1.0) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[1] + next_version = next(versions) + next_version_def = repo_def[next_version] # Prepare for a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a patch level release candidate (v0.1.1-rc.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[2] + next_version = next(versions) + next_version_def = repo_def[next_version] # Prepare for a major feature release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a major feature release candidate (v1.0.0-rc.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[3] + next_version = next(versions) + next_version_def = repo_def[next_version] # Add non-breaking feature commit - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a major feature release (v1.0.0) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[4] + next_version = next(versions) + next_version_def = repo_def[next_version] # Change to a dev branch git_repo.create_head("dev") git_repo.heads.dev.checkout() # Prepare for a minor bump release candidate - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a minor bump release candidate (v1.1.0-rc.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[5] + next_version = next(versions) + next_version_def = repo_def[next_version] # Make a patch level commit - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a 2nd release candidate (v1.1.0-rc.2) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[6] + next_version = next(versions) + next_version_def = repo_def[next_version] # Change to a feature branch git_repo.create_head("feature") git_repo.heads.feature.checkout() # Make a feature commit - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make an alpha prerelease (v1.1.0-alpha.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[7] + next_version = next(versions) + next_version_def = repo_def[next_version] # Make a another feature commit - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a 2nd alpha prerelease (v1.1.0-alpha.2) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[8] + next_version = next(versions) + next_version_def = repo_def[next_version] # Make a patch level commit - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a 3rd alpha prerelease (v1.1.0-alpha.3) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) + + return repo_dir, hvcs return _build_git_flow_repo_w_3_release_channels @@ -470,7 +462,7 @@ def cached_repo_w_git_flow_n_3_release_channels_tag_commits( def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( cached_repo_w_git_flow_n_3_release_channels_angular_commits_tag_format: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_git_flow_n_3_release_channels_angular_commits_tag_format.exists(): @@ -486,7 +478,7 @@ def repo_with_git_flow_and_release_channels_angular_commits_using_tag_format( def repo_with_git_flow_and_release_channels_angular_commits( cached_repo_w_git_flow_n_3_release_channels_angular_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_git_flow_n_3_release_channels_angular_commits.exists(): @@ -502,7 +494,7 @@ def repo_with_git_flow_and_release_channels_angular_commits( def repo_with_git_flow_and_release_channels_emoji_commits( cached_repo_w_git_flow_n_3_release_channels_emoji_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_git_flow_n_3_release_channels_emoji_commits.exists(): @@ -518,7 +510,7 @@ def repo_with_git_flow_and_release_channels_emoji_commits( def repo_with_git_flow_and_release_channels_scipy_commits( cached_repo_w_git_flow_n_3_release_channels_scipy_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_git_flow_n_3_release_channels_scipy_commits.exists(): @@ -534,7 +526,7 @@ def repo_with_git_flow_and_release_channels_scipy_commits( def repo_with_git_flow_and_release_channels_tag_commits( cached_repo_w_git_flow_n_3_release_channels_tag_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_git_flow_n_3_release_channels_tag_commits.exists(): diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 0c1dd8539..cfa992f7a 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -6,23 +6,27 @@ import pytest from git import Repo -from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory +from tests.const import EXAMPLE_HVCS_DOMAIN +from tests.util import copy_dir_tree, temporary_working_directory if TYPE_CHECKING: from pathlib import Path + from semantic_release.hvcs import HvcsBase from tests.conftest import TeardownCachedDirFn - from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseParserFn + from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( BaseRepoVersionDef, BuildRepoFn, CommitConvention, + CreateReleaseFn, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, RepoDefinition, + SimulateChangeCommitsNReturnChangelogEntryFn, + TomlSerializableTypes, VersionStr, ) @@ -150,115 +154,109 @@ def _get_versions_for_github_flow_repo_w_feature_release_channel() -> ( @pytest.fixture(scope="session") def build_github_flow_repo_w_feature_release_channel( get_commits_for_github_flow_repo_w_feature_release_channel: GetRepoDefinitionFn, - cached_example_git_project: Path, - update_pyproject_toml: UpdatePyprojectTomlFn, - use_angular_parser: UseParserFn, - use_emoji_parser: UseParserFn, - use_scipy_parser: UseParserFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_github_flow_repo_w_feature_release_channel( - git_repo_path: Path | str, - commit_type: CommitConvention, + dest_dir: Path | str, + commit_type: CommitConvention = "angular", + hvcs_client_name: str = "github", + hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, - ) -> None: - repo_definition = get_commits_for_github_flow_repo_w_feature_release_channel( - commit_type + extra_configs: dict[str, TomlSerializableTypes] | None = None, + ) -> tuple[Path, HvcsBase]: + repo_dir, hvcs = build_configured_base_repo( + dest_dir, + commit_type=commit_type, + hvcs_client_name=hvcs_client_name, + hvcs_domain=hvcs_domain, + tag_format_str=tag_format_str, + extra_configs={ + # branch "beta-testing" has prerelease suffix of "beta" + "tool.semantic_release.branches.beta-testing": { + "match": "beta.*", + "prerelease": True, + "prerelease_token": "beta", + }, + **(extra_configs or {}), + }, ) - tag_format = tag_format_str or default_tag_format_str - versions = list(repo_definition.keys()) - next_version = versions[0] - if not cached_example_git_project.exists(): - raise RuntimeError("Unable to find cached files directory!") + # Retrieve/Define project vars that will be used to create the repo below + repo_def = get_commits_for_github_flow_repo_w_feature_release_channel(commit_type) + versions = (key for key in repo_def) + next_version = next(versions) + next_version_def = repo_def[next_version] - copy_dir_tree(cached_example_git_project, git_repo_path) - - with temporary_working_directory(git_repo_path), Repo(".") as git_repo: - update_pyproject_toml( - "tool.semantic_release.branches.beta-testing", - {"match": "beta.*", "prerelease": True, "prerelease_token": "beta"}, - ) + # must be after build_configured_base_repo() so we dont set the + # default tag format in the pyproject.toml (we want semantic-release to use its defaults) + # however we need it to manually create the tags it knows how to parse + tag_format = tag_format_str or default_tag_format_str - if commit_type == "angular": - use_angular_parser() - elif commit_type == "emoji": - use_emoji_parser() - elif commit_type == "scipy": - use_scipy_parser() - elif commit_type == "tag": - use_tag_parser() - else: - raise ValueError(f"Unknown commit type: {commit_type}") - - git_repo.git.commit( - a=True, - m=repo_definition[next_version][0], # Initial commit + with temporary_working_directory(repo_dir), Repo(".") as git_repo: + # commit initial files & update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs ) # Make initial feature release (v0.1.0) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[1] + next_version = next(versions) + next_version_def = repo_def[next_version] # Make a patch level commit - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a patch level release candidate (v0.1.1-rc.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[2] + next_version = next(versions) + next_version_def = repo_def[next_version] # Make a minor level commit - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a minor level release candidate (v0.2.0-rc.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[3] + next_version = next(versions) + next_version_def = repo_def[next_version] # Make a minor level commit - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a minor level release (v0.2.0) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) # Increment version pointer - next_version = versions[4] + next_version = next(versions) + next_version_def = repo_def[next_version] # Checkout beta_testing branch git_repo.create_head("beta_testing") git_repo.heads.beta_testing.checkout() # Make a feature level commit - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=repo_definition[next_version][0]) + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a feature level beta release (v0.3.0-beta.1) - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) + + return repo_dir, hvcs return _build_github_flow_repo_w_feature_release_channel @@ -329,7 +327,7 @@ def cached_repo_w_github_flow_w_feature_release_channel_tag_commits( def repo_w_github_flow_w_feature_release_channel_angular_commits( cached_repo_w_github_flow_w_feature_release_channel_angular_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_w_github_flow_w_feature_release_channel_angular_commits.exists(): diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 2ad9c1932..56782f616 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -6,14 +6,16 @@ import pytest from git import Repo -from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory +from tests.const import EXAMPLE_HVCS_DOMAIN +from tests.util import copy_dir_tree, temporary_working_directory if TYPE_CHECKING: from pathlib import Path + from semantic_release.hvcs import HvcsBase from tests.conftest import TeardownCachedDirFn - from tests.fixtures.example_project import ExProjectDir, UseParserFn + from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( BaseRepoVersionDef, BuildRepoFn, @@ -22,6 +24,8 @@ GetRepoDefinitionFn, GetVersionStringsFn, RepoDefinition, + SimulateChangeCommitsNReturnChangelogEntryFn, + TomlSerializableTypes, VersionStr, ) @@ -120,46 +124,38 @@ def _get_versions_for_trunk_only_repo_w_no_tags() -> list[VersionStr]: @pytest.fixture(scope="session") def build_trunk_only_repo_w_no_tags( get_commits_for_trunk_only_repo_w_no_tags: GetRepoDefinitionFn, - cached_example_git_project: Path, - use_angular_parser: UseParserFn, - use_emoji_parser: UseParserFn, - use_scipy_parser: UseParserFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + build_configured_base_repo: BuildRepoFn, + simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, ) -> BuildRepoFn: def _build_trunk_only_repo_w_no_tags( - git_repo_path: Path | str, - commit_type: CommitConvention, + dest_dir: Path | str, + commit_type: CommitConvention = "angular", + hvcs_client_name: str = "github", + hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, - ) -> None: - repo_definition = get_commits_for_trunk_only_repo_w_no_tags(commit_type) - versions = list(repo_definition.keys()) - next_version = versions[0] - - if not cached_example_git_project.exists(): - raise RuntimeError("Unable to find example git project!") - - copy_dir_tree(cached_example_git_project, git_repo_path) - - with temporary_working_directory(git_repo_path), Repo(".") as git_repo: - if commit_type == "angular": - use_angular_parser() - elif commit_type == "emoji": - use_emoji_parser() - elif commit_type == "scipy": - use_scipy_parser() - elif commit_type == "tag": - use_tag_parser() - else: - raise ValueError(f"Unknown commit type: {commit_type}") - - git_repo.git.commit( - a=True, m=repo_definition[next_version][0] - ) # Initial commit - - for commit_msg in repo_definition[next_version][1:]: - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(a=True, m=commit_msg) + extra_configs: dict[str, TomlSerializableTypes] | None = None, + ) -> tuple[Path, HvcsBase]: + repo_dir, hvcs = build_configured_base_repo( + dest_dir, + commit_type=commit_type, + hvcs_client_name=hvcs_client_name, + hvcs_domain=hvcs_domain, + tag_format_str=tag_format_str, + extra_configs=extra_configs, + ) + + repo_def = get_commits_for_trunk_only_repo_w_no_tags(commit_type) + versions = (key for key in repo_def) + next_version = next(versions) + next_version_def = repo_def[next_version] + + with temporary_working_directory(repo_dir), Repo(".") as git_repo: + # Run set up commits + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) + + return repo_dir, hvcs return _build_trunk_only_repo_w_no_tags diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index 7fdb1814c..ef000809f 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -6,23 +6,27 @@ import pytest from git import Repo -from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory +from tests.const import EXAMPLE_HVCS_DOMAIN +from tests.util import copy_dir_tree, temporary_working_directory if TYPE_CHECKING: from pathlib import Path + from semantic_release.hvcs import HvcsBase from tests.conftest import TeardownCachedDirFn - from tests.fixtures.example_project import UseParserFn + from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( BaseRepoVersionDef, BuildRepoFn, CommitConvention, + CreateReleaseFn, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, RepoDefinition, + SimulateChangeCommitsNReturnChangelogEntryFn, + TomlSerializableTypes, VersionStr, ) @@ -132,91 +136,84 @@ def _get_versions_for_trunk_only_repo_w_prerelease_tags() -> list[VersionStr]: @pytest.fixture(scope="session") def build_trunk_only_repo_w_prerelease_tags( get_commits_for_trunk_only_repo_w_prerelease_tags: GetRepoDefinitionFn, - cached_example_git_project: Path, - use_angular_parser: UseParserFn, - use_emoji_parser: UseParserFn, - use_scipy_parser: UseParserFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_trunk_only_repo_w_prerelease_tags( - git_repo_path: Path | str, - commit_type: CommitConvention, + dest_dir: Path | str, + commit_type: CommitConvention = "angular", + hvcs_client_name: str = "github", + hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, - ) -> None: - repo_definition = get_commits_for_trunk_only_repo_w_prerelease_tags(commit_type) + extra_configs: dict[str, TomlSerializableTypes] | None = None, + ) -> tuple[Path, HvcsBase]: + repo_dir, hvcs = build_configured_base_repo( + dest_dir, + commit_type=commit_type, + hvcs_client_name=hvcs_client_name, + hvcs_domain=hvcs_domain, + tag_format_str=tag_format_str, + extra_configs=extra_configs, + ) + + repo_def = get_commits_for_trunk_only_repo_w_prerelease_tags(commit_type) + versions = (key for key in repo_def) + next_version = next(versions) + next_version_def = repo_def[next_version] + + # must be after build_configured_base_repo() so we dont set the + # default tag format in the pyproject.toml (we want semantic-release to use its defaults) + # however we need it to manually create the tags it knows how to parse tag_format = tag_format_str or default_tag_format_str - versions = list(repo_definition.keys()) - next_version = versions[0] - - if not cached_example_git_project.exists(): - raise RuntimeError("Unable to find cached example git project!") - - copy_dir_tree(cached_example_git_project, git_repo_path) - - with temporary_working_directory(git_repo_path), Repo(".") as git_repo: - if commit_type == "angular": - use_angular_parser() - elif commit_type == "emoji": - use_emoji_parser() - elif commit_type == "scipy": - use_scipy_parser() - elif commit_type == "tag": - use_tag_parser() - else: - raise ValueError(f"Unknown commit type: {commit_type}") - - git_repo.git.commit( - a=True, m=repo_definition[next_version][0] - ) # Initial commit - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) - - next_version = versions[1] - - # Prepare for a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit( - m=repo_definition[next_version][0] - ) # patch level commit - - # Make a prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) - - next_version = versions[2] - - # Prepare a 2nd prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit( - m=repo_definition[next_version][0] - ) # minor level commit - - # Make a 2nd prerelease - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) - - next_version = versions[3] - - # Prepare for a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit( - m=repo_definition[next_version][0] - ) # minor level commit + + with temporary_working_directory(repo_dir), Repo(".") as git_repo: + # commit initial files & update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) + + # Make initial feature release (v0.1.0) + create_release_tagged_commit(git_repo, next_version, tag_format) + + # Increment version pointer + next_version = next(versions) + next_version_def = repo_def[next_version] + + # Add a patch level change + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) + + # Make a patch level release candidate (v0.1.1-rc.1) + create_release_tagged_commit(git_repo, next_version, tag_format) + + # Increment version pointer + next_version = next(versions) + next_version_def = repo_def[next_version] + + # Make a minor level change + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) + + # Make the next feature level prerelease (v0.2.0-rc.1) + create_release_tagged_commit(git_repo, next_version, tag_format) + + # Increment version pointer + next_version = next(versions) + next_version_def = repo_def[next_version] + + # Make a minor level change + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) # Make a full release - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + create_release_tagged_commit(git_repo, next_version, tag_format) + + return repo_dir, hvcs return _build_trunk_only_repo_w_prerelease_tags @@ -287,7 +284,7 @@ def cached_repo_with_single_branch_and_prereleases_tag_commits( def repo_with_single_branch_and_prereleases_angular_commits( cached_repo_with_single_branch_and_prereleases_angular_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_with_single_branch_and_prereleases_angular_commits.exists(): @@ -303,7 +300,7 @@ def repo_with_single_branch_and_prereleases_angular_commits( def repo_with_single_branch_and_prereleases_emoji_commits( cached_repo_with_single_branch_and_prereleases_emoji_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_with_single_branch_and_prereleases_emoji_commits.exists(): @@ -319,7 +316,7 @@ def repo_with_single_branch_and_prereleases_emoji_commits( def repo_with_single_branch_and_prereleases_scipy_commits( cached_repo_with_single_branch_and_prereleases_scipy_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_with_single_branch_and_prereleases_scipy_commits.exists(): @@ -335,7 +332,7 @@ def repo_with_single_branch_and_prereleases_scipy_commits( def repo_with_single_branch_and_prereleases_tag_commits( cached_repo_with_single_branch_and_prereleases_tag_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_with_single_branch_and_prereleases_tag_commits.exists(): diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index da11a0ea8..6d4ba14ee 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -6,23 +6,27 @@ import pytest from git import Repo -from tests.const import COMMIT_MESSAGE -from tests.util import add_text_to_file, copy_dir_tree, temporary_working_directory +from tests.const import EXAMPLE_HVCS_DOMAIN +from tests.util import copy_dir_tree, temporary_working_directory if TYPE_CHECKING: from pathlib import Path + from semantic_release.hvcs import HvcsBase from tests.conftest import TeardownCachedDirFn - from tests.fixtures.example_project import UseParserFn + from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( BaseRepoVersionDef, BuildRepoFn, CommitConvention, + CreateReleaseFn, ExProjectGitRepoFn, GetRepoDefinitionFn, GetVersionStringsFn, RepoDefinition, + SimulateChangeCommitsNReturnChangelogEntryFn, + TomlSerializableTypes, VersionStr, ) @@ -100,61 +104,61 @@ def _get_versions_for_trunk_only_repo_w_tags() -> list[VersionStr]: @pytest.fixture(scope="session") def build_trunk_only_repo_w_tags( get_commits_for_trunk_only_repo_w_tags: GetRepoDefinitionFn, - cached_example_git_project: Path, - use_angular_parser: UseParserFn, - use_emoji_parser: UseParserFn, - use_scipy_parser: UseParserFn, - use_tag_parser: UseParserFn, - file_in_repo: str, + build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_trunk_only_repo_w_tags( - git_repo_path: Path | str, - commit_type: CommitConvention, + dest_dir: Path | str, + commit_type: CommitConvention = "angular", + hvcs_client_name: str = "github", + hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, tag_format_str: str | None = None, - ) -> None: - repo_definition = get_commits_for_trunk_only_repo_w_tags(commit_type) + extra_configs: dict[str, TomlSerializableTypes] | None = None, + ) -> tuple[Path, HvcsBase]: + repo_dir, hvcs = build_configured_base_repo( + dest_dir, + commit_type=commit_type, + hvcs_client_name=hvcs_client_name, + hvcs_domain=hvcs_domain, + tag_format_str=tag_format_str, + extra_configs=extra_configs, + ) + + repo_def = get_commits_for_trunk_only_repo_w_tags(commit_type) + versions = (key for key in repo_def) + next_version = next(versions) + next_version_def = repo_def[next_version] + + # must be after build_configured_base_repo() so we dont set the + # default tag format in the pyproject.toml (we want semantic-release to use its defaults) + # however we need it to manually create the tags it knows how to parse tag_format = tag_format_str or default_tag_format_str - versions = list(repo_definition.keys()) - next_version = versions[0] - - if not cached_example_git_project.exists(): - raise RuntimeError("Unable to find example git project!") - - copy_dir_tree(cached_example_git_project, git_repo_path) - - with temporary_working_directory(git_repo_path), Repo(".") as git_repo: - if commit_type == "angular": - use_angular_parser() - elif commit_type == "emoji": - use_emoji_parser() - elif commit_type == "scipy": - use_scipy_parser() - elif commit_type == "tag": - use_tag_parser() - else: - raise ValueError(f"Unknown commit type: {commit_type}") - - git_repo.git.commit( - a=True, m=repo_definition[next_version][0] - ) # Initial commit - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) - - next_version = versions[1] - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit( - m=repo_definition[next_version][0] - ) # patch level message - - add_text_to_file(git_repo, file_in_repo) - git_repo.git.commit(m=COMMIT_MESSAGE.format(version=next_version)) - tag_str = tag_format.format(version=next_version) - git_repo.git.tag(tag_str, m=tag_str) + + # Run Git operations to simulate repo commit & release history + with temporary_working_directory(repo_dir), Repo(".") as git_repo: + # commit initial files & update commit msg with sha & url + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) + + # Publish initial feature release (v0.1.0) [updates tool.poetry.version] + create_release_tagged_commit(git_repo, next_version, tag_format) + + # Increment version pointer + next_version = next(versions) + next_version_def = repo_def[next_version] + + # Add a patch level change + next_version_def["commits"] = simulate_change_commits_n_rtn_changelog_entry( + git_repo, next_version_def["commits"], hvcs + ) + + # Make a patch level release (v0.1.1) + create_release_tagged_commit(git_repo, next_version, tag_format) + + return repo_dir, hvcs return _build_trunk_only_repo_w_tags @@ -225,7 +229,7 @@ def cached_repo_with_single_branch_tag_commits( def repo_with_single_branch_angular_commits( cached_repo_with_single_branch_angular_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_with_single_branch_angular_commits.exists(): @@ -238,7 +242,7 @@ def repo_with_single_branch_angular_commits( def repo_with_single_branch_emoji_commits( cached_repo_with_single_branch_emoji_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_with_single_branch_emoji_commits.exists(): @@ -251,7 +255,7 @@ def repo_with_single_branch_emoji_commits( def repo_with_single_branch_scipy_commits( cached_repo_with_single_branch_scipy_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_with_single_branch_scipy_commits.exists(): @@ -264,7 +268,7 @@ def repo_with_single_branch_scipy_commits( def repo_with_single_branch_tag_commits( cached_repo_with_single_branch_tag_commits: Path, example_project_git_repo: ExProjectGitRepoFn, - example_project_dir: Path, + example_project_dir: ExProjectDir, change_to_ex_proj_dir: None, ) -> Repo: if not cached_repo_with_single_branch_tag_commits.exists(): From 710d96482edae38438c090e5e631f2d6b6e990f2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 13:25:52 -0500 Subject: [PATCH 087/167] style(fixtures): update styling of imports on git repo fixture --- tests/fixtures/git_repo.py | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 41af3c2d5..cb698707e 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -6,17 +6,30 @@ import pytest from git import Actor, Repo -from tests.const import COMMIT_MESSAGE, EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER -from tests.util import add_text_to_file, copy_dir_tree, shortuid, temporary_working_directory +from tests.const import ( + COMMIT_MESSAGE, + EXAMPLE_HVCS_DOMAIN, + EXAMPLE_REPO_NAME, + EXAMPLE_REPO_OWNER, +) +from tests.util import ( + add_text_to_file, + copy_dir_tree, + shortuid, + temporary_working_directory, +) if TYPE_CHECKING: - from typing import Generator, Literal, TypedDict, Protocol, Union + from typing import Generator, Literal, Protocol, TypedDict, Union from semantic_release.hvcs import HvcsBase from tests.conftest import TeardownCachedDirFn from tests.fixtures.example_project import ( - ExProjectDir, UpdatePyprojectTomlFn, UseHvcsFn, UseParserFn + ExProjectDir, + UpdatePyprojectTomlFn, + UseHvcsFn, + UseParserFn, ) CommitConvention = Literal["angular", "emoji", "scipy", "tag"] @@ -31,21 +44,24 @@ class RepoVersionDef(TypedDict): Used for builder functions that only need to know about a single commit convention type """ + changelog_sections: list[ChangelogTypeHeadingDef] commits: list[CommitMsg] + class ChangelogTypeHeadingDef(TypedDict): section: ChangelogTypeHeading i_commits: list[int] """List of indexes values to match to the commits list in the RepoVersionDef""" + class BaseRepoVersionDef(TypedDict): - """ - A Common Repo definition for a get_commits_repo_*() fixture with all commit convention types - """ + """A Common Repo definition for a get_commits_repo_*() fixture with all commit convention types""" + changelog_sections: dict[CommitConvention, list[ChangelogTypeHeadingDef]] commits: list[dict[CommitConvention, CommitMsg]] + class BuildRepoFn(Protocol): def __call__( self, @@ -58,30 +74,36 @@ def __call__( ) -> tuple[Path, HvcsBase]: ... + class CommitNReturnChangelogEntryFn(Protocol): def __call__( self, git_repo: Repo, commit_msg: str, hvcs: HvcsBase ) -> str: ... + class SimulateChangeCommitsNReturnChangelogEntryFn(Protocol): def __call__( self, git_repo: Repo, commit_msgs: list[CommitMsg], hvcs: HvcsBase ) -> list[CommitMsg]: ... + class CreateReleaseFn(Protocol): def __call__(self, git_repo: Repo, version: str, tag_format: str = ...) -> None: ... + class ExProjectGitRepoFn(Protocol): def __call__(self) -> Repo: ... + class GetVersionStringsFn(Protocol): def __call__(self) -> list[VersionStr]: ... + RepoDefinition = dict[VersionStr, RepoVersionDef] """ A Type alias to define a repositories versions, commits, and changelog sections @@ -251,7 +273,7 @@ def _build_configured_base_repo( extra_configs: dict[str, TomlSerializableTypes] | None = None, ) -> tuple[Path, HvcsBase]: if not cached_example_git_project.exists(): - raise RuntimeError(f"Unable to find cached git project files!") + raise RuntimeError("Unable to find cached git project files!") # Copy the cached git project the dest directory copy_dir_tree(cached_example_git_project, dest_dir) From d2314f86946df8a6ef30ca7a5bd163c5989e46f7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 12 Feb 2024 12:51:07 +0000 Subject: [PATCH 088/167] style: beautify 710d96482edae38438c090e5e631f2d6b6e990f2 --- tests/fixtures/example_project.py | 3 ++ tests/fixtures/git_repo.py | 31 +++++++------------ .../git_flow/repo_w_3_release_channels.py | 4 +-- .../github_flow/repo_w_release_channels.py | 4 ++- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index ebc5b2694..6d79ba402 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -324,6 +324,7 @@ def _use_tag_parser() -> type[CommitParser]: @pytest.fixture(scope="session") def use_github_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use GitHub as the HVCS.""" + def _use_github_hvcs(domain: str | None = None) -> type[HvcsBase]: update_pyproject_toml("tool.semantic_release.remote.type", "github") if domain is not None: @@ -336,6 +337,7 @@ def _use_github_hvcs(domain: str | None = None) -> type[HvcsBase]: @pytest.fixture(scope="session") def use_gitlab_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use GitLab as the HVCS.""" + def _use_gitlab_hvcs(domain: str | None = None) -> type[HvcsBase]: update_pyproject_toml("tool.semantic_release.remote.type", "gitlab") if domain is not None: @@ -348,6 +350,7 @@ def _use_gitlab_hvcs(domain: str | None = None) -> type[HvcsBase]: @pytest.fixture(scope="session") def use_gitea_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: """Modify the configuration file to use Gitea as the HVCS.""" + def _use_gitea_hvcs(domain: str | None = None) -> type[HvcsBase]: update_pyproject_toml("tool.semantic_release.remote.type", "gitea") if domain is not None: diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index cb698707e..7c55b5a2c 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -48,20 +48,17 @@ class RepoVersionDef(TypedDict): changelog_sections: list[ChangelogTypeHeadingDef] commits: list[CommitMsg] - class ChangelogTypeHeadingDef(TypedDict): section: ChangelogTypeHeading i_commits: list[int] """List of indexes values to match to the commits list in the RepoVersionDef""" - class BaseRepoVersionDef(TypedDict): """A Common Repo definition for a get_commits_repo_*() fixture with all commit convention types""" changelog_sections: dict[CommitConvention, list[ChangelogTypeHeadingDef]] commits: list[dict[CommitConvention, CommitMsg]] - class BuildRepoFn(Protocol): def __call__( self, @@ -74,36 +71,28 @@ def __call__( ) -> tuple[Path, HvcsBase]: ... - class CommitNReturnChangelogEntryFn(Protocol): - def __call__( - self, git_repo: Repo, commit_msg: str, hvcs: HvcsBase - ) -> str: + def __call__(self, git_repo: Repo, commit_msg: str, hvcs: HvcsBase) -> str: ... - class SimulateChangeCommitsNReturnChangelogEntryFn(Protocol): def __call__( self, git_repo: Repo, commit_msgs: list[CommitMsg], hvcs: HvcsBase ) -> list[CommitMsg]: ... - class CreateReleaseFn(Protocol): def __call__(self, git_repo: Repo, version: str, tag_format: str = ...) -> None: ... - class ExProjectGitRepoFn(Protocol): def __call__(self) -> Repo: ... - class GetVersionStringsFn(Protocol): def __call__(self) -> list[VersionStr]: ... - RepoDefinition = dict[VersionStr, RepoVersionDef] """ A Type alias to define a repositories versions, commits, and changelog sections @@ -173,10 +162,13 @@ def _commit_n_rtn_changelog_entry( # log commit in changelog format after commit action commit_sha = git_repo.head.commit.hexsha - return str.join(" ", [ - str(git_repo.head.commit.message).strip(), - f"([`{commit_sha[:7]}`]({hvcs.commit_hash_url(commit_sha)}))" - ]) + return str.join( + " ", + [ + str(git_repo.head.commit.message).strip(), + f"([`{commit_sha[:7]}`]({hvcs.commit_hash_url(commit_sha)}))", + ], + ) return _commit_n_rtn_changelog_entry @@ -264,6 +256,7 @@ def build_configured_base_repo( for when the test executes semantic_release. It returns a function so that derivative fixtures can call this fixture with individual parameters. """ + def _build_configured_base_repo( dest_dir: Path | str, commit_type: str = "angular", @@ -280,7 +273,6 @@ def _build_configured_base_repo( # Make sure we are in the dest directory with temporary_working_directory(dest_dir): - # Set parser configuration if commit_type == "angular": use_angular_parser() @@ -308,7 +300,9 @@ def _build_configured_base_repo( # Set tag format in configuration if tag_format_str is not None: - update_pyproject_toml("tool.semantic_release.tag_format", tag_format_str) + update_pyproject_toml( + "tool.semantic_release.tag_format", tag_format_str + ) # Apply configurations to pyproject.toml if extra_configs is not None: @@ -320,7 +314,6 @@ def _build_configured_base_repo( return _build_configured_base_repo - @pytest.fixture def example_project_git_repo( example_project_dir: ExProjectDir, diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index b6929b652..e333b0615 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -177,7 +177,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: "tag": ":nut_and_bolt: (feature) add some more text", }, ], - } + }, } def _get_commits_for_git_flow_repo_w_3_release_channels( @@ -240,7 +240,7 @@ def _build_git_flow_repo_w_3_release_channels( "tool.semantic_release.branches.dev": { "match": "dev", "prerelease": True, - "prerelease_token": "rc" + "prerelease_token": "rc", }, # branch "feature" has prerelease suffix of "alpha" "tool.semantic_release.branches.features": { diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index cfa992f7a..0b2c2cd97 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -185,7 +185,9 @@ def _build_github_flow_repo_w_feature_release_channel( ) # Retrieve/Define project vars that will be used to create the repo below - repo_def = get_commits_for_github_flow_repo_w_feature_release_channel(commit_type) + repo_def = get_commits_for_github_flow_repo_w_feature_release_channel( + commit_type + ) versions = (key for key in repo_def) next_version = next(versions) next_version_def = repo_def[next_version] From bbbbfebff33dd24b8aed2d894de958d532eac596 Mon Sep 17 00:00:00 2001 From: Beat Reichenbach Date: Wed, 27 Dec 2023 17:10:05 -0800 Subject: [PATCH 089/167] feat: add bitbucket hvcs --- docs/configuration.rst | 4 +- semantic_release/cli/config.py | 2 + semantic_release/hvcs/__init__.py | 3 +- semantic_release/hvcs/bitbucket.py | 125 +++++++++++ .../semantic_release/hvcs/test_bitbucket.py | 198 ++++++++++++++++++ 5 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 semantic_release/hvcs/bitbucket.py create mode 100644 tests/unit/semantic_release/hvcs/test_bitbucket.py diff --git a/docs/configuration.rst b/docs/configuration.rst index 1ff72fb5a..4dc6a4e44 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -567,8 +567,8 @@ Name of the remote to push to using ``git push -u $name `` """""""""""""" The type of the remote VCS. Currently, Python Semantic Release supports ``"github"``, -``"gitlab"`` and ``"gitea"``. Not all functionality is available with all remote types, -but we welcome pull requests to help improve this! +``"gitlab"``, ``"gitea"`` and ``"bitbucket"``. Not all functionality is available with all +remote types, but we welcome pull requests to help improve this! **Default:** ``"github"`` diff --git a/semantic_release/cli/config.py b/semantic_release/cli/config.py index 2162f6633..f918bd7b6 100644 --- a/semantic_release/cli/config.py +++ b/semantic_release/cli/config.py @@ -43,6 +43,7 @@ class HvcsClient(str, Enum): + BITBUCKET = "bitbucket" GITHUB = "github" GITLAB = "gitlab" GITEA = "gitea" @@ -57,6 +58,7 @@ class HvcsClient(str, Enum): _known_hvcs: Dict[HvcsClient, Type[hvcs.HvcsBase]] = { + HvcsClient.BITBUCKET: hvcs.Bitbucket, HvcsClient.GITHUB: hvcs.Github, HvcsClient.GITLAB: hvcs.Gitlab, HvcsClient.GITEA: hvcs.Gitea, diff --git a/semantic_release/hvcs/__init__.py b/semantic_release/hvcs/__init__.py index c3c69a1ac..449d92e9f 100644 --- a/semantic_release/hvcs/__init__.py +++ b/semantic_release/hvcs/__init__.py @@ -1,7 +1,8 @@ from semantic_release.hvcs._base import HvcsBase +from semantic_release.hvcs.bitbucket import Bitbucket from semantic_release.hvcs.gitea import Gitea from semantic_release.hvcs.github import Github from semantic_release.hvcs.gitlab import Gitlab from semantic_release.hvcs.token_auth import TokenAuth -__all__ = ["Gitea", "Github", "Gitlab", "HvcsBase", "TokenAuth"] +__all__ = ["Bitbucket", "Gitea", "Github", "Gitlab", "HvcsBase", "TokenAuth"] diff --git a/semantic_release/hvcs/bitbucket.py b/semantic_release/hvcs/bitbucket.py new file mode 100644 index 000000000..7e4efceb1 --- /dev/null +++ b/semantic_release/hvcs/bitbucket.py @@ -0,0 +1,125 @@ +"""Helper code for interacting with a Bitbucket remote VCS""" + +# Note: Bitbucket doesn't support releases. But it allows users to use +# `semantic-release version` without having to specify `--no-vcs-release`. + +from __future__ import annotations + +import logging +import mimetypes +import os +from functools import lru_cache + +from semantic_release.hvcs._base import HvcsBase +from semantic_release.hvcs.token_auth import TokenAuth +from semantic_release.hvcs.util import build_requests_session + +log = logging.getLogger(__name__) + +# Add a mime type for wheels +# Fix incorrect entries in the `mimetypes` registry. +# On Windows, the Python standard library's `mimetypes` reads in +# mappings from file extension to MIME type from the Windows +# registry. Other applications can and do write incorrect values +# to this registry, which causes `mimetypes.guess_type` to return +# incorrect values, which causes TensorBoard to fail to render on +# the frontend. +# This method hard-codes the correct mappings for certain MIME +# types that are known to be either used by python-semantic-release or +# problematic in general. +mimetypes.add_type("application/octet-stream", ".whl") +mimetypes.add_type("text/markdown", ".md") + + +class Bitbucket(HvcsBase): + """Bitbucket helper class""" + + API_VERSION = "2.0" + DEFAULT_DOMAIN = "bitbucket.org" + DEFAULT_API_DOMAIN = "api.bitbucket.org" + DEFAULT_ENV_TOKEN_NAME = "BITBUCKET_TOKEN" + + def __init__( + self, + remote_url: str, + hvcs_domain: str | None = None, + hvcs_api_domain: str | None = None, + token: str | None = None, + ) -> None: + self._remote_url = remote_url + + if hvcs_domain is not None: + self.hvcs_domain = hvcs_domain + else: + api_url = os.getenv("BITBUCKET_SERVER_URL", self.DEFAULT_DOMAIN) + self.hvcs_domain = api_url.replace("https://", "") + + # ref: https://developer.atlassian.com/cloud/bitbucket/rest/intro/#uri-uuid + if hvcs_api_domain is not None: + self.hvcs_api_domain = hvcs_api_domain + else: + api_url = os.getenv("BITBUCKET_API_URL", self.DEFAULT_API_DOMAIN) + self.hvcs_api_domain = api_url.replace("https://", "") + + self.api_url = f"https://{self.hvcs_api_domain}/{self.API_VERSION}" + + self.token = token + auth = None if not self.token else TokenAuth(self.token) + self.session = build_requests_session(auth=auth) + + @lru_cache(maxsize=1) + def _get_repository_owner_and_name(self) -> tuple[str, str]: + # ref: https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ + if "BITBUCKET_REPO_FULL_NAME" in os.environ: + log.info("Getting repository owner and name from environment variables.") + owner, name = os.environ["BITBUCKET_REPO_FULL_NAME"].rsplit("/", 1) + return owner, name + return super()._get_repository_owner_and_name() + + def compare_url(self, from_rev: str, to_rev: str) -> str: + """ + Get the Bitbucket comparison link between two version tags. + :param from_rev: The older version to compare. + :param to_rev: The newer version to compare. + :return: Link to view a comparison between the two versions. + """ + return ( + f"https://{self.hvcs_domain}/{self.owner}/{self.repo_name}/" + f"branches/compare/{from_rev}%0D{to_rev}" + ) + + def remote_url(self, use_token: bool = True) -> str: + if not use_token: + # Note: Assume the user is using SSH. + return self._remote_url + if not self.token: + raise ValueError("Requested to use token but no token set.") + user = os.environ.get("BITBUCKET_USER") + if user: + # Note: If the user is set, assume the token is an app secret. This will work + # on any repository the user has access to. + # https://support.atlassian.com/bitbucket-cloud/docs/push-back-to-your-repository + return ( + f"https://{user}:{self.token}@" + f"{self.hvcs_domain}/{self.owner}/{self.repo_name}.git" + ) + else: + # Note: Assume the token is a repository token which will only work on the + # repository it was created for. + # https://support.atlassian.com/bitbucket-cloud/docs/using-access-tokens + return ( + f"https://x-token-auth:{self.token}@" + f"{self.hvcs_domain}/{self.owner}/{self.repo_name}.git" + ) + + def commit_hash_url(self, commit_hash: str) -> str: + return ( + f"https://{self.hvcs_domain}/{self.owner}/{self.repo_name}/" + f"commits/{commit_hash}" + ) + + def pull_request_url(self, pr_number: str | int) -> str: + return ( + f"https://{self.hvcs_domain}/{self.owner}/{self.repo_name}/" + f"pull-requests/{pr_number}" + ) diff --git a/tests/unit/semantic_release/hvcs/test_bitbucket.py b/tests/unit/semantic_release/hvcs/test_bitbucket.py new file mode 100644 index 000000000..4a1756e55 --- /dev/null +++ b/tests/unit/semantic_release/hvcs/test_bitbucket.py @@ -0,0 +1,198 @@ +import os +from unittest import mock + +import pytest +from requests import Session + +from semantic_release.hvcs.bitbucket import Bitbucket +from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER + + +@pytest.fixture +def default_bitbucket_client(): + remote_url = f"git@bitbucket.org:{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}.git" + return Bitbucket(remote_url=remote_url) + + +@pytest.mark.parametrize( + ( + "patched_os_environ, hvcs_domain, hvcs_api_domain, " + "expected_hvcs_domain, expected_hvcs_api_domain" + ), + [ + ({}, None, None, Bitbucket.DEFAULT_DOMAIN, Bitbucket.DEFAULT_API_DOMAIN), + ( + {"BITBUCKET_SERVER_URL": "https://special.custom.server/vcs/"}, + None, + None, + "special.custom.server/vcs/", + Bitbucket.DEFAULT_API_DOMAIN, + ), + ( + {"BITBUCKET_API_URL": "https://api.special.custom.server/"}, + None, + None, + Bitbucket.DEFAULT_DOMAIN, + "api.special.custom.server/", + ), + ( + {"BITBUCKET_SERVER_URL": "https://special.custom.server/vcs/"}, + "https://example.com", + None, + "https://example.com", + Bitbucket.DEFAULT_API_DOMAIN, + ), + ( + {"BITBUCKET_API_URL": "https://api.special.custom.server/"}, + None, + "https://api.example.com", + Bitbucket.DEFAULT_DOMAIN, + "https://api.example.com", + ), + ], +) +@pytest.mark.parametrize( + "remote_url", + [ + f"git@bitbucket.org:{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}.git", + f"https://bitbucket.org/{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}.git", + ], +) +@pytest.mark.parametrize("token", ("abc123", None)) +def test_bitbucket_client_init( + patched_os_environ, + hvcs_domain, + hvcs_api_domain, + expected_hvcs_domain, + expected_hvcs_api_domain, + remote_url, + token, +): + with mock.patch.dict(os.environ, patched_os_environ, clear=True): + client = Bitbucket( + remote_url=remote_url, + hvcs_domain=hvcs_domain, + hvcs_api_domain=hvcs_api_domain, + token=token, + ) + + assert client.hvcs_domain == expected_hvcs_domain + assert client.hvcs_api_domain == expected_hvcs_api_domain + assert client.api_url == f"https://{client.hvcs_api_domain}/2.0" + assert client.token == token + assert client._remote_url == remote_url + assert hasattr(client, "session") + assert isinstance(getattr(client, "session", None), Session) + + +@pytest.mark.parametrize( + "patched_os_environ, expected_owner, expected_name", + [ + ({}, None, None), + ({"BITBUCKET_REPO_FULL_NAME": "path/to/repo/foo"}, "path/to/repo", "foo"), + ], +) +def test_bitbucket_get_repository_owner_and_name( + default_bitbucket_client, patched_os_environ, expected_owner, expected_name +): + with mock.patch.dict(os.environ, patched_os_environ, clear=True): + if expected_owner is None and expected_name is None: + assert ( + default_bitbucket_client._get_repository_owner_and_name() + == super( + Bitbucket, default_bitbucket_client + )._get_repository_owner_and_name() + ) + else: + assert default_bitbucket_client._get_repository_owner_and_name() == ( + expected_owner, + expected_name, + ) + + +def test_compare_url(default_bitbucket_client): + assert default_bitbucket_client.compare_url( + from_rev="revA", to_rev="revB" + ) == "https://{domain}/{owner}/{repo}/branches/compare/revA%0DrevB".format( + domain=default_bitbucket_client.hvcs_domain, + owner=default_bitbucket_client.owner, + repo=default_bitbucket_client.repo_name, + ) + + +@pytest.mark.parametrize( + "patched_os_environ, use_token, token, _remote_url, expected", + [ + ( + {"BITBUCKET_USER": "foo"}, + False, + "", + "git@bitbucket.org:custom/example.git", + "git@bitbucket.org:custom/example.git", + ), + ( + {}, + False, + "aabbcc", + "git@bitbucket.org:custom/example.git", + "git@bitbucket.org:custom/example.git", + ), + ( + {}, + True, + "aabbcc", + "git@bitbucket.org:custom/example.git", + "https://x-token-auth:aabbcc@bitbucket.org/custom/example.git", + ), + ( + {"BITBUCKET_USER": "foo"}, + False, + "aabbcc", + "git@bitbucket.org:custom/example.git", + "git@bitbucket.org:custom/example.git", + ), + ( + {"BITBUCKET_USER": "foo"}, + True, + "aabbcc", + "git@bitbucket.org:custom/example.git", + "https://foo:aabbcc@bitbucket.org/custom/example.git", + ), + ], +) +def test_remote_url( + patched_os_environ, + use_token, + token, + _remote_url, + expected, + default_bitbucket_client, +): + with mock.patch.dict(os.environ, patched_os_environ, clear=True): + default_bitbucket_client._remote_url = _remote_url + default_bitbucket_client.token = token + assert default_bitbucket_client.remote_url(use_token=use_token) == expected + + +def test_commit_hash_url(default_bitbucket_client): + sha = "244f7e11bcb1e1ce097db61594056bc2a32189a0" + assert default_bitbucket_client.commit_hash_url( + sha + ) == "https://{domain}/{owner}/{repo}/commits/{sha}".format( + domain=default_bitbucket_client.hvcs_domain, + owner=default_bitbucket_client.owner, + repo=default_bitbucket_client.repo_name, + sha=sha, + ) + + +@pytest.mark.parametrize("pr_number", (420, "420")) +def test_pull_request_url(default_bitbucket_client, pr_number): + assert default_bitbucket_client.pull_request_url( + pr_number=pr_number + ) == "https://{domain}/{owner}/{repo}/pull-requests/{pr_number}".format( + domain=default_bitbucket_client.hvcs_domain, + owner=default_bitbucket_client.owner, + repo=default_bitbucket_client.repo_name, + pr_number=pr_number, + ) From b240e129b180d45c1d63d464283b7dfbcb641d0c Mon Sep 17 00:00:00 2001 From: Beat Reichenbach Date: Wed, 27 Dec 2023 17:10:16 -0800 Subject: [PATCH 090/167] docs: fix typo --- semantic_release/hvcs/_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/semantic_release/hvcs/_base.py b/semantic_release/hvcs/_base.py index 05a459c5c..3a825a1d7 100644 --- a/semantic_release/hvcs/_base.py +++ b/semantic_release/hvcs/_base.py @@ -98,7 +98,7 @@ def create_release( def get_release_id_by_tag(self, tag: str) -> int | None: """ Given a Git tag, return the ID (as the remote VCS defines it) of a corresponding - release in the remove VCS, if supported + release in the remote VCS, if supported """ _not_supported(self, "get_release_id_by_tag") return None From c33f8ff37e007a944b5fb1884b9e05b33ccd2047 Mon Sep 17 00:00:00 2001 From: Beat Reichenbach Date: Thu, 28 Dec 2023 01:09:53 -0800 Subject: [PATCH 091/167] test: add bitbucket to changelog unit test --- .../unit/semantic_release/changelog/test_changelog_context.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/semantic_release/changelog/test_changelog_context.py b/tests/unit/semantic_release/changelog/test_changelog_context.py index f90cc7432..dcb62124c 100644 --- a/tests/unit/semantic_release/changelog/test_changelog_context.py +++ b/tests/unit/semantic_release/changelog/test_changelog_context.py @@ -4,7 +4,7 @@ from semantic_release.changelog import environment, make_changelog_context from semantic_release.changelog.release_history import ReleaseHistory -from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.hvcs import Bitbucket, Gitea, Github, Gitlab from semantic_release.version.translator import VersionTranslator NULL_HEX_SHA = Object.NULL_HEX_SHA @@ -128,7 +128,7 @@ ), ], ) -@pytest.mark.parametrize("hvcs_client_class", (Github, Gitlab, Gitea)) +@pytest.mark.parametrize("hvcs_client_class", (Github, Gitlab, Gitea, Bitbucket)) @pytest.mark.usefixtures("expected_changelog") def test_changelog_context(repo, changelog_template, commit_parser, hvcs_client_class): # NOTE: this test only checks that the changelog can be rendered with the From a5168e40b9a14dbd022f62964f382b39faf1e0df Mon Sep 17 00:00:00 2001 From: Beat Reichenbach Date: Fri, 29 Dec 2023 14:09:34 -0800 Subject: [PATCH 092/167] fix: remove unofficial environment variables --- semantic_release/hvcs/bitbucket.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/semantic_release/hvcs/bitbucket.py b/semantic_release/hvcs/bitbucket.py index 7e4efceb1..8258314e0 100644 --- a/semantic_release/hvcs/bitbucket.py +++ b/semantic_release/hvcs/bitbucket.py @@ -47,22 +47,12 @@ def __init__( token: str | None = None, ) -> None: self._remote_url = remote_url - - if hvcs_domain is not None: - self.hvcs_domain = hvcs_domain - else: - api_url = os.getenv("BITBUCKET_SERVER_URL", self.DEFAULT_DOMAIN) - self.hvcs_domain = api_url.replace("https://", "") - + self.hvcs_domain = hvcs_domain or self.DEFAULT_DOMAIN.replace("https://", "") # ref: https://developer.atlassian.com/cloud/bitbucket/rest/intro/#uri-uuid - if hvcs_api_domain is not None: - self.hvcs_api_domain = hvcs_api_domain - else: - api_url = os.getenv("BITBUCKET_API_URL", self.DEFAULT_API_DOMAIN) - self.hvcs_api_domain = api_url.replace("https://", "") - + self.hvcs_api_domain = hvcs_api_domain or self.DEFAULT_API_DOMAIN.replace( + "https://", "" + ) self.api_url = f"https://{self.hvcs_api_domain}/{self.API_VERSION}" - self.token = token auth = None if not self.token else TokenAuth(self.token) self.session = build_requests_session(auth=auth) From b78a387d8eccbc1a6a424a183254fc576126199c Mon Sep 17 00:00:00 2001 From: Beat Reichenbach Date: Fri, 29 Dec 2023 14:10:21 -0800 Subject: [PATCH 093/167] docs: add bitbucket authentication --- docs/index.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index 158743ad3..7c7cd12b1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -142,6 +142,19 @@ Gitea (``GITEA_TOKEN``) A personal access token from Gitea. This token should be stored in the ``GITEA_TOKEN`` environment variable. +Bitbucket (``BITBUCKET_TOKEN``) +""""""""""""""""""""""""""""""" + +Bitbucket does not support uploading releases but can still benefit from automated tags +and changelogs. The user has three options to push changes to the repository: + +#. Use SSH keys. +#. Use an `App Secret`_, store the secret in the ``BITBUCKET_TOKEN`` environment variable and the username in ``BITBUCKET_USER``. +#. Use an `Access Token`_ for the repository and store it in the ``BITBUCKET_TOKEN`` environment variable. + +.. _App Secret: https://support.atlassian.com/bitbucket-cloud/docs/push-back-to-your-repository/#App-secret +.. _Access Token: https://support.atlassian.com/bitbucket-cloud/docs/repository-access-tokens + .. seealso:: - :ref:`Changelog ` - customize your project's changelog. - :ref:`Customizing VCS Release Notes ` - customize From 5c3fe694de5db0fc6a157a6a7717fa8c4d26f6f6 Mon Sep 17 00:00:00 2001 From: Beat Reichenbach Date: Wed, 3 Jan 2024 11:30:37 -0800 Subject: [PATCH 094/167] test: remove environment variable tests --- .../semantic_release/hvcs/test_bitbucket.py | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/tests/unit/semantic_release/hvcs/test_bitbucket.py b/tests/unit/semantic_release/hvcs/test_bitbucket.py index 4a1756e55..b38f40e06 100644 --- a/tests/unit/semantic_release/hvcs/test_bitbucket.py +++ b/tests/unit/semantic_release/hvcs/test_bitbucket.py @@ -19,37 +19,7 @@ def default_bitbucket_client(): "patched_os_environ, hvcs_domain, hvcs_api_domain, " "expected_hvcs_domain, expected_hvcs_api_domain" ), - [ - ({}, None, None, Bitbucket.DEFAULT_DOMAIN, Bitbucket.DEFAULT_API_DOMAIN), - ( - {"BITBUCKET_SERVER_URL": "https://special.custom.server/vcs/"}, - None, - None, - "special.custom.server/vcs/", - Bitbucket.DEFAULT_API_DOMAIN, - ), - ( - {"BITBUCKET_API_URL": "https://api.special.custom.server/"}, - None, - None, - Bitbucket.DEFAULT_DOMAIN, - "api.special.custom.server/", - ), - ( - {"BITBUCKET_SERVER_URL": "https://special.custom.server/vcs/"}, - "https://example.com", - None, - "https://example.com", - Bitbucket.DEFAULT_API_DOMAIN, - ), - ( - {"BITBUCKET_API_URL": "https://api.special.custom.server/"}, - None, - "https://api.example.com", - Bitbucket.DEFAULT_DOMAIN, - "https://api.example.com", - ), - ], + [({}, None, None, Bitbucket.DEFAULT_DOMAIN, Bitbucket.DEFAULT_API_DOMAIN)], ) @pytest.mark.parametrize( "remote_url", From 55d6e0349303e7b1a6a598a8f4eb54d708d1b27d Mon Sep 17 00:00:00 2001 From: Beat Reichenbach Date: Wed, 3 Jan 2024 18:16:59 -0800 Subject: [PATCH 095/167] refactor: add lint workaround --- tests/unit/semantic_release/hvcs/test_bitbucket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/semantic_release/hvcs/test_bitbucket.py b/tests/unit/semantic_release/hvcs/test_bitbucket.py index b38f40e06..f645c8eca 100644 --- a/tests/unit/semantic_release/hvcs/test_bitbucket.py +++ b/tests/unit/semantic_release/hvcs/test_bitbucket.py @@ -134,7 +134,7 @@ def test_remote_url( patched_os_environ, use_token, token, - _remote_url, + _remote_url, # noqa: PT019 expected, default_bitbucket_client, ): From 56f146d9f4c0fc7f2a84ad11b21c8c45e9221782 Mon Sep 17 00:00:00 2001 From: Beat Reichenbach Date: Wed, 3 Jan 2024 18:25:01 -0800 Subject: [PATCH 096/167] docs: add bitbucket to token table --- docs/configuration.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 4dc6a4e44..558da9cdc 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -611,13 +611,14 @@ The default value for this setting depends on what you specify as :ref:`remote.type `. Review the table below to see what the default token value will be for each remote type. -================ == ============================ +================ == =============================== ``remote.type`` Default ``remote.token`` -================ == ============================ +================ == =============================== ``"github"`` -> ``{ env = "GH_TOKEN" }`` ``"gitlab"`` -> ``{ env = "GITLAB_TOKEN" }`` ``"gitea"`` -> ``{ env = "GITEA_TOKEN" }`` -================ == ============================ +``"bitbucket"`` -> ``{ env = "BITBUCKET_TOKEN" }`` +================ == =============================== **Default:** ``{ env = "" }``, where ```` depends on :ref:`remote.type ` as indicated above. From f7f7c8a8d499126a16238e7183cde54e3ea4cea0 Mon Sep 17 00:00:00 2001 From: Beat Reichenbach Date: Mon, 12 Feb 2024 07:44:00 -0800 Subject: [PATCH 097/167] style: ruff linter --- tests/unit/semantic_release/hvcs/test_bitbucket.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/semantic_release/hvcs/test_bitbucket.py b/tests/unit/semantic_release/hvcs/test_bitbucket.py index f645c8eca..2b76312ce 100644 --- a/tests/unit/semantic_release/hvcs/test_bitbucket.py +++ b/tests/unit/semantic_release/hvcs/test_bitbucket.py @@ -5,6 +5,7 @@ from requests import Session from semantic_release.hvcs.bitbucket import Bitbucket + from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER From 82bfcd3ba619ce6bda0eb79b903be48d3ac5ff2c Mon Sep 17 00:00:00 2001 From: semantic-release Date: Wed, 14 Feb 2024 19:50:02 +0000 Subject: [PATCH 098/167] 9.1.0 Automatically generated by python-semantic-release --- CHANGELOG.md | 95 ++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 97 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e082be14..3fa76e5c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,101 @@ +## v9.1.0 (2024-02-14) + +### Build + +* build(deps): bump minimum required `tomlkit` to `>=0.11.0` + +TOMLDocument is missing the `unwrap()` function in `v0.10.2` which +causes an AttributeError to occur when attempting to read a the text +in `pyproject.toml` as discovered with #834 + +Resolves: #834 ([`291aace`](https://github.com/python-semantic-release/python-semantic-release/commit/291aacea1d0429a3b27e92b0a20b598f43f6ea6b)) + +### Documentation + +* docs: add bitbucket to token table ([`56f146d`](https://github.com/python-semantic-release/python-semantic-release/commit/56f146d9f4c0fc7f2a84ad11b21c8c45e9221782)) + +* docs: add bitbucket authentication ([`b78a387`](https://github.com/python-semantic-release/python-semantic-release/commit/b78a387d8eccbc1a6a424a183254fc576126199c)) + +* docs: fix typo ([`b240e12`](https://github.com/python-semantic-release/python-semantic-release/commit/b240e129b180d45c1d63d464283b7dfbcb641d0c)) + +### Feature + +* feat: add bitbucket hvcs ([`bbbbfeb`](https://github.com/python-semantic-release/python-semantic-release/commit/bbbbfebff33dd24b8aed2d894de958d532eac596)) + +### Fix + +* fix: remove unofficial environment variables ([`a5168e4`](https://github.com/python-semantic-release/python-semantic-release/commit/a5168e40b9a14dbd022f62964f382b39faf1e0df)) + +### Refactor + +* refactor: add lint workaround ([`55d6e03`](https://github.com/python-semantic-release/python-semantic-release/commit/55d6e0349303e7b1a6a598a8f4eb54d708d1b27d)) + +### Style + +* style: ruff linter ([`f7f7c8a`](https://github.com/python-semantic-release/python-semantic-release/commit/f7f7c8a8d499126a16238e7183cde54e3ea4cea0)) + +* style: beautify 710d96482edae38438c090e5e631f2d6b6e990f2 ([`d2314f8`](https://github.com/python-semantic-release/python-semantic-release/commit/d2314f86946df8a6ef30ca7a5bd163c5989e46f7)) + +* style(fixtures): update styling of imports on git repo fixture ([`710d964`](https://github.com/python-semantic-release/python-semantic-release/commit/710d96482edae38438c090e5e631f2d6b6e990f2)) + +* style: beautify 8e3f87ce801498fdbb0c1b2261b9768941c0a21c ([`16c057a`](https://github.com/python-semantic-release/python-semantic-release/commit/16c057a8da471a696f6d6484f2cc821fd883f5ed)) + +* style(tests): update typing & import issues ([`2597ea5`](https://github.com/python-semantic-release/python-semantic-release/commit/2597ea584ab5084ef6ab398b9ec6186a6191ac1f)) + +### Test + +* test: remove environment variable tests ([`5c3fe69`](https://github.com/python-semantic-release/python-semantic-release/commit/5c3fe694de5db0fc6a157a6a7717fa8c4d26f6f6)) + +* test: add bitbucket to changelog unit test ([`c33f8ff`](https://github.com/python-semantic-release/python-semantic-release/commit/c33f8ff37e007a944b5fb1884b9e05b33ccd2047)) + +* test(fixtures): refactor repos to use common fixtures to simplify workflow ([`9ad8296`](https://github.com/python-semantic-release/python-semantic-release/commit/9ad829693589e0cfe0962159f41cc2efd886eacc)) + +* test(fixtures): add common repo build/setup fixture ([`8da2840`](https://github.com/python-semantic-release/python-semantic-release/commit/8da284095173f7bcece768e0452d8d18378454d4)) + +* test(fixtures): apply new repo definition to repos ([`1e13e2c`](https://github.com/python-semantic-release/python-semantic-release/commit/1e13e2cee25b2760058bdd58238a9f55d5999c5e)) + +* test(fixtures): define new repo definition type ([`e0e8792`](https://github.com/python-semantic-release/python-semantic-release/commit/e0e8792d9dcd3b7d3f4a41ef0f8fe2c92a761f55)) + +* test(fixtures): add generic multi-commit executor from definition ([`e9605f3`](https://github.com/python-semantic-release/python-semantic-release/commit/e9605f37b001ff478f08f781587448c16ba31687)) + +* test(fixtures): add commit & changelog entry derivation function ([`29dbc7c`](https://github.com/python-semantic-release/python-semantic-release/commit/29dbc7cf6f9a066d9f6bd785fbeb11e700a586ac)) + +* test(fixtures): add manual release commit creation fixture ([`93dc523`](https://github.com/python-semantic-release/python-semantic-release/commit/93dc523776af303e0870f42618bb1590948fb826)) + +* test(fixtures): expand hvcs use fixtures to set remote.domain value ([`1303a5a`](https://github.com/python-semantic-release/python-semantic-release/commit/1303a5a5fcaec326f2cbe625d847c7de821a853d)) + +* test(fixtures): use const to set example project domain ([`1a15761`](https://github.com/python-semantic-release/python-semantic-release/commit/1a15761c32de93b03628be0032f6a57f196d6ee1)) + +* test(constants): set starting example project version to 0.0.0 ([`310af11`](https://github.com/python-semantic-release/python-semantic-release/commit/310af1150129e9d6ef833dd9a18860119b15ee01)) + +* test(fixtures): remove unused fixtures & types ([`8e3f87c`](https://github.com/python-semantic-release/python-semantic-release/commit/8e3f87ce801498fdbb0c1b2261b9768941c0a21c)) + +* test(util): add util func for removing readonly .git/* files ([`180a053`](https://github.com/python-semantic-release/python-semantic-release/commit/180a053a992c0ffa03e1d9800cc4bf743d6331b2)) + +* test(fixtures): rename repo main and feature branches fixture across tests ([`f006e2b`](https://github.com/python-semantic-release/python-semantic-release/commit/f006e2b2e9e2025a3b07d14d79714420d59a63fe)) + +* test(fixtures): add caching to github flow development repos ([`693b143`](https://github.com/python-semantic-release/python-semantic-release/commit/693b14352da4fa2c7fcd969c4b8c5230ec90055c)) + +* test(fixtures): add caching to git flow development repos ([`3db33f0`](https://github.com/python-semantic-release/python-semantic-release/commit/3db33f0c6556701de54ecc07dde91b65fc5d60a5)) + +* test(fixtures): add caching to trunk development w/ tags repos ([`4e3b6b6`](https://github.com/python-semantic-release/python-semantic-release/commit/4e3b6b6ef3f493ca613e25b5a5bdb5545fa12e9f)) + +* test(fixtures): refactor for session level fixture use ([`a42b032`](https://github.com/python-semantic-release/python-semantic-release/commit/a42b032ff44bedee81d20765163758c8a2ca6153)) + +* test(utils): add a utility to temporary change directory ([`68e12f3`](https://github.com/python-semantic-release/python-semantic-release/commit/68e12f369deced9df9e2c8d08dae7a4e4ece3fee)) + +* test(fixtures): modularize git repo file into sub-modules ([`18d0877`](https://github.com/python-semantic-release/python-semantic-release/commit/18d0877f6a6db38ed127b1c07d191075dda46904)) + +* test(fixtures): deconflict colliding fixtures for file dependent fixture execution ([`1890cf2`](https://github.com/python-semantic-release/python-semantic-release/commit/1890cf2e8989e5fea182894e663c3659edf6903a)) + +* test(fixtures): cache the base example git directory ([`0cd0f44`](https://github.com/python-semantic-release/python-semantic-release/commit/0cd0f44019a5dacf01040b6917b35f2a918d00ca)) + +* test(fixtures): adapt cli conftest fixtures to read when file is known to exist ([`0193cde`](https://github.com/python-semantic-release/python-semantic-release/commit/0193cde64cf7e07a7ecd5a0ea9513105f6207386)) + + ## v9.0.3 (2024-02-08) ### Chore diff --git a/pyproject.toml b/pyproject.toml index dd99fd184..c6149b40a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.0.3" +version = "9.1.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index b41020466..fe03c9f53 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "9.0.3" +__version__ = "9.1.0" def setup_hook(argv: list[str]) -> None: From 1c25b8e6f1e43c15ca7d5a59dca0a13767f9bc33 Mon Sep 17 00:00:00 2001 From: KernelA <17554646+KernelA@users.noreply.github.com> Date: Fri, 16 Feb 2024 19:13:04 +0300 Subject: [PATCH 099/167] fix(parse_git_url): fix bad url with dash --- semantic_release/helpers.py | 2 +- tests/unit/semantic_release/test_helpers.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/semantic_release/helpers.py b/semantic_release/helpers.py index fb0070d9a..482413e48 100644 --- a/semantic_release/helpers.py +++ b/semantic_release/helpers.py @@ -119,7 +119,7 @@ def parse_git_url(url: str) -> ParsedGitUrl: (r"^git\+ssh://", "ssh://"), # normalize an scp like syntax to URL compatible syntax # excluding port definitions (:#####) & including numeric usernames - (r"(ssh://(?:\w+@)?[\w.]+):(?!\d{1,5}/\w+/)(.*)$", r"\1/\2"), + (r"(ssh://(?:\w+@)?[\w.-]+):(?!\d{1,5}/\w+/)(.*)$", r"\1/\2"), # normalize implicit file (windows || posix) urls to explicit file:// urls (r"^([C-Z]:/)|^/(\w)", r"file:///\1\2"), ] diff --git a/tests/unit/semantic_release/test_helpers.py b/tests/unit/semantic_release/test_helpers.py index 1e37833fe..a8ab92736 100644 --- a/tests/unit/semantic_release/test_helpers.py +++ b/tests/unit/semantic_release/test_helpers.py @@ -10,6 +10,10 @@ "http://git.mycompany.com/username/myproject.git", ParsedGitUrl("http", "git.mycompany.com", "username", "myproject"), ), + ( + "http://subsubdomain.subdomain.company-net.com/username/myproject.git", + ParsedGitUrl("http", "subsubdomain.subdomain.company-net.com", "username", "myproject"), + ), ( "https://github.com/username/myproject.git", ParsedGitUrl("https", "github.com", "username", "myproject"), @@ -22,6 +26,10 @@ "https://git.mycompany.com:4443/username/myproject.git", ParsedGitUrl("https", "git.mycompany.com:4443", "username", "myproject"), ), + ( + "https://subsubdomain.subdomain.company-net.com/username/myproject.git", + ParsedGitUrl("https", "subsubdomain.subdomain.company-net.com", "username", "myproject"), + ), ( "git://host.xz/path/to/repo.git/", ParsedGitUrl("git", "host.xz", "path/to", "repo"), @@ -34,6 +42,10 @@ "git@github.com:username/myproject.git", ParsedGitUrl("ssh", "git@github.com", "username", "myproject"), ), + ( + "git@subsubdomain.subdomain.company-net.com:username/myproject.git", + ParsedGitUrl("ssh", "git@subsubdomain.subdomain.company-net.com", "username", "myproject"), + ), ( "ssh://git@github.com:3759/myproject.git", ParsedGitUrl("ssh", "git@github.com", "3759", "myproject"), @@ -46,6 +58,10 @@ "ssh://git@bitbucket.org:7999/username/myproject.git", ParsedGitUrl("ssh", "git@bitbucket.org:7999", "username", "myproject"), ), + ( + "ssh://git@subsubdomain.subdomain.company-net.com:username/myproject.git", + ParsedGitUrl("ssh", "git@subsubdomain.subdomain.company-net.com", "username", "myproject"), + ), ( "git+ssh://git@github.com:username/myproject.git", ParsedGitUrl("ssh", "git@github.com", "username", "myproject"), From 85b2a2f5cf4c49aa3e394dfe3574e24266f93bfc Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 25 Feb 2024 08:46:36 +0000 Subject: [PATCH 100/167] style: beautify 1c25b8e6f1e43c15ca7d5a59dca0a13767f9bc33 --- tests/unit/semantic_release/test_helpers.py | 28 ++++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/unit/semantic_release/test_helpers.py b/tests/unit/semantic_release/test_helpers.py index a8ab92736..ee8ffdee3 100644 --- a/tests/unit/semantic_release/test_helpers.py +++ b/tests/unit/semantic_release/test_helpers.py @@ -12,7 +12,12 @@ ), ( "http://subsubdomain.subdomain.company-net.com/username/myproject.git", - ParsedGitUrl("http", "subsubdomain.subdomain.company-net.com", "username", "myproject"), + ParsedGitUrl( + "http", + "subsubdomain.subdomain.company-net.com", + "username", + "myproject", + ), ), ( "https://github.com/username/myproject.git", @@ -28,7 +33,12 @@ ), ( "https://subsubdomain.subdomain.company-net.com/username/myproject.git", - ParsedGitUrl("https", "subsubdomain.subdomain.company-net.com", "username", "myproject"), + ParsedGitUrl( + "https", + "subsubdomain.subdomain.company-net.com", + "username", + "myproject", + ), ), ( "git://host.xz/path/to/repo.git/", @@ -44,7 +54,12 @@ ), ( "git@subsubdomain.subdomain.company-net.com:username/myproject.git", - ParsedGitUrl("ssh", "git@subsubdomain.subdomain.company-net.com", "username", "myproject"), + ParsedGitUrl( + "ssh", + "git@subsubdomain.subdomain.company-net.com", + "username", + "myproject", + ), ), ( "ssh://git@github.com:3759/myproject.git", @@ -60,7 +75,12 @@ ), ( "ssh://git@subsubdomain.subdomain.company-net.com:username/myproject.git", - ParsedGitUrl("ssh", "git@subsubdomain.subdomain.company-net.com", "username", "myproject"), + ParsedGitUrl( + "ssh", + "git@subsubdomain.subdomain.company-net.com", + "username", + "myproject", + ), ), ( "git+ssh://git@github.com:username/myproject.git", From c82bd673c30da94a02e039963c8c4c8a51758096 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sun, 25 Feb 2024 08:48:42 +0000 Subject: [PATCH 101/167] 9.1.1 Automatically generated by python-semantic-release --- CHANGELOG.md | 11 +++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fa76e5c7..3cb840697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ +## v9.1.1 (2024-02-25) + +### Fix + +* fix(parse_git_url): fix bad url with dash ([`1c25b8e`](https://github.com/python-semantic-release/python-semantic-release/commit/1c25b8e6f1e43c15ca7d5a59dca0a13767f9bc33)) + +### Style + +* style: beautify 1c25b8e6f1e43c15ca7d5a59dca0a13767f9bc33 ([`85b2a2f`](https://github.com/python-semantic-release/python-semantic-release/commit/85b2a2f5cf4c49aa3e394dfe3574e24266f93bfc)) + + ## v9.1.0 (2024-02-14) ### Build diff --git a/pyproject.toml b/pyproject.toml index c6149b40a..74c37290b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.1.0" +version = "9.1.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index fe03c9f53..a51843a1b 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "9.1.0" +__version__ = "9.1.1" def setup_hook(argv: list[str]) -> None: From 814240c7355df95e9be9a6ed31d004b800584bc0 Mon Sep 17 00:00:00 2001 From: Robert Minsk Date: Mon, 11 Mar 2024 00:29:47 -0700 Subject: [PATCH 102/167] feat(version): add new version print flags to display the last released version and tag --- docs/commands.rst | 27 ++++++++++++ semantic_release/cli/commands/version.py | 54 +++++++++++++++++++++--- tests/command_line/test_version.py | 33 +++++++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) diff --git a/docs/commands.rst b/docs/commands.rst index 1e9359b61..185f00015 100644 --- a/docs/commands.rst +++ b/docs/commands.rst @@ -144,6 +144,33 @@ command line options:: semantic-release version --patch --print semantic-release version --prerelease --print +.. _cmd-version-option-print-tag: + +``--print-tag`` +*************** + +Same as the :ref:`cmd-version-option-print` flag but prints the complete tag +name (ex. ``v1.0.0`` or ``py-v1.0.0``) instead of the raw version number +(``1.0.0``). + +.. _cmd-version-option-print-last-released: + +``--print-last-released`` +************************* + +Print the last released version based on the Git tags. This flag is useful if you just +want to see the released version without determining what the next version will be. +Note if the version can not be found nothing will be printed. + +.. _cmd-version-option-print-last-released-tag: + +``--print-last-released-tag`` +*************** + +Same as the :ref:`cmd-version-option-print-last-released` flag but prints the +complete tag name (ex. ``v1.0.0`` or ``py-v1.0.0``) instead of the raw version +number (``1.0.0``). + .. _cmd-version-option-force-level: ``--major/--minor/--patch`` diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index 8f422b120..83a4364fd 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -9,6 +9,7 @@ import click import shellingham # type: ignore[import] +from click_option_group import MutuallyExclusiveOptionGroup, optgroup from git.exc import GitCommandError from semantic_release.changelog import ReleaseHistory, environment, recursive_render @@ -26,8 +27,9 @@ log = logging.getLogger(__name__) -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from git import Repo + from git.refs.tag import Tag from semantic_release.cli.config import RuntimeContext from semantic_release.version import VersionTranslator @@ -48,6 +50,13 @@ def is_forced_prerelease( return force_prerelease or ((force_level is None) and prerelease) +def last_released( + repo: Repo, translator: VersionTranslator +) -> tuple[Tag, Version] | None: + ts_and_vs = tags_and_versions(repo.tags, translator) + return ts_and_vs[0] if ts_and_vs else None + + def version_from_forced_level( repo: Repo, level_bump: LevelBump, translator: VersionTranslator ) -> Version: @@ -111,9 +120,26 @@ def shell(cmd: str, *, check: bool = True) -> subprocess.CompletedProcess: "help_option_names": ["-h", "--help"], }, ) -@click.option( +@optgroup.group("Print flags", cls=MutuallyExclusiveOptionGroup) +@optgroup.option( "--print", "print_only", is_flag=True, help="Print the next version and exit" ) +@optgroup.option( + "--print-tag", + "print_only_tag", + is_flag=True, + help="Print the next version tag and exit", +) +@optgroup.option( + "--print-last-released", + is_flag=True, + help="Print the last released version and exit", +) +@optgroup.option( + "--print-last-released-tag", + is_flag=True, + help="Print the last released version tag and exit", +) @click.option( "--prerelease", "force_prerelease", @@ -191,6 +217,9 @@ def shell(cmd: str, *, check: bool = True) -> subprocess.CompletedProcess: def version( # noqa: C901 ctx: click.Context, print_only: bool = False, + print_only_tag: bool = False, + print_last_released: bool = False, + print_last_released_tag: bool = False, force_prerelease: bool = False, prerelease_token: str | None = None, force_level: str | None = None, @@ -219,8 +248,20 @@ def version( # noqa: C901 """ runtime: RuntimeContext = ctx.obj repo = runtime.repo - parser = runtime.commit_parser translator = runtime.version_translator + + # We can short circuit updating the release if we are only printing the last released version + if print_last_released or print_last_released_tag: + if last_release := last_released(repo, translator): + if print_last_released: + click.echo(last_release[1]) + if print_last_released_tag: + click.echo(last_release[0]) + else: + log.warning("No release tags found.") + ctx.exit(0) + + parser = runtime.commit_parser prerelease = is_forced_prerelease( force_prerelease=force_prerelease, force_level=force_level, @@ -301,7 +342,10 @@ def version( # noqa: C901 ctx.call_on_close(gha_output.write_if_possible) # Print the new version so that command-line output capture will work - click.echo(str(new_version)) + if print_only_tag: + click.echo(translator.str_to_tag(str(new_version))) + else: + click.echo(str(new_version)) # If the new version has already been released, we fail and abort if strict; # otherwise we exit with 0. @@ -318,7 +362,7 @@ def version( # noqa: C901 ) ctx.exit(0) - if print_only: + if print_only or print_only_tag: ctx.exit(0) rprint(f"[bold green]The next version is: [white]{new_version!s}[/white]! :rocket:") diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index ba2fbeaed..6f62e24fd 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -743,3 +743,36 @@ def test_version_only_update_files_no_git_actions( assert len(removed) == 1 assert re.match('__version__ = ".*"', removed[0]) assert added == [f'__version__ = "{expected_new_version}"\n'] + + +def test_version_print_last_released_prints_version( + repo_with_single_branch_tag_commits: Repo, cli_runner: CliRunner +): + result = cli_runner.invoke(main, [version.name, "--print-last-released"]) + assert result.exit_code == 0 + assert result.stdout == "0.1.1\n" + + +def test_version_print_last_released_prints_released_if_commits( + repo_with_single_branch_tag_commits: Repo, + example_project_dir: ExProjectDir, + cli_runner: CliRunner, +): + new_file = example_project_dir / "temp.txt" + new_file.write_text("test --print-last-released") + + repo_with_single_branch_tag_commits.git.add(str(new_file.resolve())) + repo_with_single_branch_tag_commits.git.commit(m="fix: temp new file") + + result = cli_runner.invoke(main, [version.name, "--print-last-released"]) + assert result.exit_code == 0 + assert result.stdout == "0.1.1\n" + + +def test_version_print_last_released_prints_nothing_if_no_tags( + caplog, repo_with_no_tags_angular_commits: Repo, cli_runner: CliRunner +): + result = cli_runner.invoke(main, [version.name, "--print-last-released"]) + assert result.exit_code == 0 + assert result.stdout == "" + assert "No release tags found." in caplog.text From bd892b89c26df9fccc9335c84e2b3217e3e02a37 Mon Sep 17 00:00:00 2001 From: Robert Minsk Date: Mon, 11 Mar 2024 00:37:21 -0700 Subject: [PATCH 103/167] build(deps): add click-option-group for grouping exclusive flags --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 74c37290b..c8f892919 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ readme = "README.rst" authors = [{ name = "Rolf Erik Lekang", email = "me@rolflekang.com" }] dependencies = [ "click>=8,<9", + "click-option-group~=0.5", "gitpython>=3.0.8,<4", "requests>=2.25,<3", "jinja2>=3.1.2,<4", From 228347c8f64cc46f01b717ccdc2daf15384c7f2e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 10 Mar 2024 11:37:15 -0400 Subject: [PATCH 104/167] build(MANIFEST): fix sdist contents to include docs & tests --- MANIFEST.in | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 257ad0ed9..e09e8dd87 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,9 @@ -include README.md MANIFEST.in LICENSE pyproject.toml -recursive-include *.j2 +# Make sure Jinja templates are included +recursive-include ./ *.j2 + +# Make sure non-python files are included graft semantic_release/data + +# include docs & testing into sdist, ignored for wheel build +graft tests/ +graft docs/ From a040aa43eb2218d76b3e56d280c7853633af4f45 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 22:15:13 -0500 Subject: [PATCH 105/167] chore(dependabot): adjust conf to relax bumping flexible requirement specs --- .github/dependabot.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 07d648f87..3161cb209 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1,6 +1,7 @@ --- version: 2 updates: + - package-ecosystem: "pip" directory: "/" schedule: @@ -14,6 +15,8 @@ updates: - dependencies open-pull-requests-limit: 10 rebase-strategy: auto + versioning-strategy: "increase-if-necessary" + - package-ecosystem: "github-actions" directory: "/" schedule: From dedb3b765c8530379af61d3046c3bb9c160d54e5 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 11:08:53 -0500 Subject: [PATCH 106/167] feat(version-config): add option to disable 0.x.x versions --- semantic_release/cli/commands/version.py | 1 + semantic_release/cli/config.py | 3 ++ semantic_release/version/algorithm.py | 38 ++++++++++++++++-------- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index 83a4364fd..e14e9e7b1 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -332,6 +332,7 @@ def version( # noqa: C901 commit_parser=parser, prerelease=prerelease, major_on_zero=major_on_zero, + allow_zero_version=runtime.allow_zero_version, ) if build_metadata: diff --git a/semantic_release/cli/config.py b/semantic_release/cli/config.py index f918bd7b6..39229f1c0 100644 --- a/semantic_release/cli/config.py +++ b/semantic_release/cli/config.py @@ -145,6 +145,7 @@ class RawConfig(BaseModel): commit_parser_options: Dict[str, Any] = {} logging_use_named_masks: bool = False major_on_zero: bool = True + allow_zero_version: bool = True remote: RemoteConfig = RemoteConfig() tag_format: str = "v{version}" publish: PublishConfig = PublishConfig() @@ -229,6 +230,7 @@ class RuntimeContext: commit_parser: CommitParser[ParseResult, ParserOptions] version_translator: VersionTranslator major_on_zero: bool + allow_zero_version: bool prerelease: bool assets: List[str] commit_author: Actor @@ -414,6 +416,7 @@ def from_raw_config( commit_parser=commit_parser, version_translator=version_translator, major_on_zero=raw.major_on_zero, + allow_zero_version=raw.allow_zero_version, build_command=raw.build_command, version_declarations=tuple(version_declarations), hvcs_client=hvcs_client, diff --git a/semantic_release/version/algorithm.py b/semantic_release/version/algorithm.py index 63f381bf0..f4783a3c5 100644 --- a/semantic_release/version/algorithm.py +++ b/semantic_release/version/algorithm.py @@ -143,6 +143,7 @@ def _increment_version( prerelease: bool, prerelease_token: str, major_on_zero: bool, + allow_zero_version: bool, ) -> Version: """ Using the given versions, along with a given `level_bump`, increment to @@ -158,19 +159,29 @@ def _increment_version( `latest_full_version_in_history`, correspondingly, is the latest full release which is in this branch's history. """ + local_vars = list(locals().items()) log.debug( - "_increment_version: %s", ", ".join(f"{k} = {v}" for k, v in locals().items()) + "_increment_version: %s", ", ".join(f"{k} = {v}" for k, v in local_vars) ) - if not major_on_zero and latest_version.major == 0: - # if we are a 0.x.y release and have set `major_on_zero`, - # breaking changes should increment the minor digit - # Correspondingly, we reduce the level that we increment the - # version by. - log.debug( - "reducing version increment due to 0. version and major_on_zero=False" - ) + if latest_version.major == 0: + if not allow_zero_version: + # Set up default version to be 1.0.0 if currently 0.x.x which means a commented + # breaking change is not required to bump to 1.0.0 + log.debug( + "Bumping major version as 0.x.x versions are disabled because of allow_zero_version=False" + ) + level_bump = LevelBump.MAJOR + + elif not major_on_zero: + # if we are a 0.x.y release and have set `major_on_zero`, + # breaking changes should increment the minor digit + # Correspondingly, we reduce the level that we increment the + # version by. + log.debug( + "reducing version increment due to 0. version and major_on_zero=False" + ) - level_bump = min(level_bump, LevelBump.MINOR) + level_bump = min(level_bump, LevelBump.MINOR) if prerelease: log.debug("prerelease=true") @@ -261,6 +272,7 @@ def next_version( commit_parser: CommitParser[ParseResult, ParserOptions], prerelease: bool = False, major_on_zero: bool = True, + allow_zero_version: bool = True, ) -> Version: """ Evaluate the history within `repo`, and based on the tags and commits in the repo @@ -398,8 +410,9 @@ def next_version( level_bump = max(parsed_levels, default=LevelBump.NO_RELEASE) log.info("The type of the next release release is: %s", level_bump) if level_bump is LevelBump.NO_RELEASE: - log.info("No release will be made") - return latest_version + if latest_version.major != 0 or allow_zero_version: + log.info("No release will be made") + return latest_version return _increment_version( latest_version=latest_version, @@ -418,4 +431,5 @@ def next_version( prerelease=prerelease, prerelease_token=translator.prerelease_token, major_on_zero=major_on_zero, + allow_zero_version=allow_zero_version, ) From 4028f8384a0181c8d58c81ae81cf0b241a02a710 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 11:10:05 -0500 Subject: [PATCH 107/167] docs(configuration): add description of `allow-zero-version` configuration option --- docs/configuration.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/configuration.rst b/docs/configuration.rst index 558da9cdc..8cb017b7f 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -268,6 +268,26 @@ identifying which secrets were replaced, or use a generic string to mask them. **Default:** ``false`` +.. _config-allow-zero-version: + +``allow_zero_version (bool)`` +""""""""""""""""""""""""""""" + +This flag controls whether or not Python Semantic Release will use version +numbers aligning with the ``0.x.x`` pattern. + +If set to ``true`` and starting at ``0.0.0``, a minor bump would set the +next version as ``0.1.0`` whereas a patch bump would set the next version as +``0.0.1``. A breaking change (ie. major bump) would set the next version as +``1.0.0`` unless the :ref:`major_on_zero` is set to ``false``. + +If set to ``false``, Python Semantic Release will consider the first possible +version to be ``1.0.0``, regardless of patch, minor, or major change level. +Additionally, when ``allow_zero_version`` is set to ``false``, +the :ref:`major_on_zero` setting is ignored. + +**Default:** ``true`` + .. _config-major-on-zero: ``major_on_zero (bool)`` From f7753cdabd07e276bc001478d605fca9a4b37ec4 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 11:10:46 -0500 Subject: [PATCH 108/167] docs(configuration): clarify the `major_on_zero` configuration option --- docs/configuration.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 8cb017b7f..b3a1432be 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -293,8 +293,14 @@ the :ref:`major_on_zero` setting is ignored. ``major_on_zero (bool)`` """""""""""""""""""""""" +This flag controls whether or not Python Semantic Release will increment the major +version upon a breaking change when the version matches ``0.y.z``. This value is +set to ``true`` by default, where breaking changes will increment the ``0`` major +version to ``1.0.0`` like normally expected. + If set to ``false``, major (breaking) releases will increment the minor digit of the -version while the major version is ``0``, instead of the major digit. +version while the major version is ``0``, instead of the major digit. This allows for +continued breaking changes to be made while the major version remains ``0``. From the `Semantic Versioning Specification`_: @@ -303,6 +309,11 @@ From the `Semantic Versioning Specification`_: .. _Semantic Versioning Specification: https://semver.org/spec/v2.0.0.html#spec-item-4 +When you are ready to release a stable version, set ``major_on_zero`` to ``true`` and +run Python Semantic Release again. This will increment the major version to ``1.0.0``. + +When :ref:`allow_zero_version` is set to ``false``, this setting is ignored. + **Default:** ``true`` .. _config-tag-format: From 791c69d8d9968357284d884344e1710b9289688a Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 11:39:11 -0500 Subject: [PATCH 109/167] test(unit): update unit test for incrementing version --- .../version/test_algorithm.py | 183 +++++++++--------- 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/tests/unit/semantic_release/version/test_algorithm.py b/tests/unit/semantic_release/version/test_algorithm.py index b3b98559e..253b1c98f 100644 --- a/tests/unit/semantic_release/version/test_algorithm.py +++ b/tests/unit/semantic_release/version/test_algorithm.py @@ -194,203 +194,204 @@ def test_tags_and_versions_ignores_invalid_tags_as_versions( # NOTE: level_bump != LevelBump.NO_RELEASE, we return early in the # algorithm to discount this case ( - Version.parse("1.0.0"), - Version.parse("1.0.0"), - Version.parse("1.0.0"), + "1.0.0", + "1.0.0", + "1.0.0", LevelBump.PRERELEASE_REVISION, False, "rc", - Version.parse("1.0.0-rc.1"), + "1.0.0-rc.1", ), ( - Version.parse("1.0.0"), - Version.parse("1.0.0"), - Version.parse("1.0.0"), + "1.0.0", + "1.0.0", + "1.0.0", LevelBump.PRERELEASE_REVISION, True, "rc", - Version.parse("1.0.0-rc.1"), + "1.0.0-rc.1", ), ( - Version.parse("1.0.0"), - Version.parse("1.0.0"), - Version.parse("1.0.0"), + "1.0.0", + "1.0.0", + "1.0.0", LevelBump.PATCH, False, "rc", - Version.parse("1.0.1"), + "1.0.1", ), ( - Version.parse("1.0.0"), - Version.parse("1.0.0"), - Version.parse("1.0.0"), + "1.0.0", + "1.0.0", + "1.0.0", LevelBump.PATCH, True, "rc", - Version.parse("1.0.1-rc.1"), + "1.0.1-rc.1", ), ( - Version.parse("1.0.0"), - Version.parse("1.0.0"), - Version.parse("1.0.0"), + "1.0.0", + "1.0.0", + "1.0.0", LevelBump.MINOR, False, "rc", - Version.parse("1.1.0"), + "1.1.0", ), ( - Version.parse("1.0.0"), - Version.parse("1.0.0"), - Version.parse("1.0.0"), + "1.0.0", + "1.0.0", + "1.0.0", LevelBump.MINOR, True, "rc", - Version.parse("1.1.0-rc.1"), + "1.1.0-rc.1", ), ( - Version.parse("1.0.0"), - Version.parse("1.0.0"), - Version.parse("1.0.0"), + "1.0.0", + "1.0.0", + "1.0.0", LevelBump.MAJOR, False, "rc", - Version.parse("2.0.0"), + "2.0.0", ), ( - Version.parse("1.0.0"), - Version.parse("1.0.0"), - Version.parse("1.0.0"), + "1.0.0", + "1.0.0", + "1.0.0", LevelBump.MAJOR, True, "rc", - Version.parse("2.0.0-rc.1"), + "2.0.0-rc.1", ), ( - Version.parse("1.2.4-rc.1"), - Version.parse("1.2.0"), - Version.parse("1.2.3"), + "1.2.4-rc.1", + "1.2.0", + "1.2.3", LevelBump.PATCH, False, "rc", - Version.parse("1.2.4"), + "1.2.4", ), ( - Version.parse("1.2.4-rc.1"), - Version.parse("1.2.0"), - Version.parse("1.2.3"), + "1.2.4-rc.1", + "1.2.0", + "1.2.3", LevelBump.PATCH, True, "rc", - Version.parse("1.2.4-rc.2"), + "1.2.4-rc.2", ), ( - Version.parse("1.2.4-rc.1"), - Version.parse("1.2.0"), - Version.parse("1.2.3"), + "1.2.4-rc.1", + "1.2.0", + "1.2.3", LevelBump.MINOR, False, "rc", - Version.parse("1.3.0"), + "1.3.0", ), ( - Version.parse("1.2.4-rc.1"), - Version.parse("1.2.0"), - Version.parse("1.2.3"), + "1.2.4-rc.1", + "1.2.0", + "1.2.3", LevelBump.MINOR, True, "rc", - Version.parse("1.3.0-rc.1"), + "1.3.0-rc.1", ), ( - Version.parse("1.2.4-rc.1"), - Version.parse("1.2.0"), - Version.parse("1.2.3"), + "1.2.4-rc.1", + "1.2.0", + "1.2.3", LevelBump.MAJOR, False, "rc", - Version.parse("2.0.0"), + "2.0.0", ), ( - Version.parse("1.2.4-rc.1"), - Version.parse("1.2.0"), - Version.parse("1.2.3"), + "1.2.4-rc.1", + "1.2.0", + "1.2.3", LevelBump.MAJOR, True, "rc", - Version.parse("2.0.0-rc.1"), + "2.0.0-rc.1", ), ( - Version.parse("2.0.0-rc.1"), - Version.parse("1.22.0"), - Version.parse("1.19.3"), + "2.0.0-rc.1", + "1.22.0", + "1.19.3", LevelBump.PATCH, False, "rc", - Version.parse("2.0.0"), + "2.0.0", ), ( - Version.parse("2.0.0-rc.1"), - Version.parse("1.22.0"), - Version.parse("1.19.3"), + "2.0.0-rc.1", + "1.22.0", + "1.19.3", LevelBump.PATCH, True, "rc", - Version.parse("2.0.0-rc.2"), + "2.0.0-rc.2", ), ( - Version.parse("2.0.0-rc.1"), - Version.parse("1.22.0"), - Version.parse("1.19.3"), + "2.0.0-rc.1", + "1.22.0", + "1.19.3", LevelBump.MINOR, False, "rc", - Version.parse("2.0.0"), + "2.0.0", ), ( - Version.parse("2.0.0-rc.1"), - Version.parse("1.22.0"), - Version.parse("1.19.3"), + "2.0.0-rc.1", + "1.22.0", + "1.19.3", LevelBump.MINOR, True, "rc", - Version.parse("2.0.0-rc.2"), + "2.0.0-rc.2", ), ( - Version.parse("2.0.0-rc.1"), - Version.parse("1.22.0"), - Version.parse("1.19.3"), + "2.0.0-rc.1", + "1.22.0", + "1.19.3", LevelBump.MAJOR, False, "rc", - Version.parse("2.0.0"), + "2.0.0", ), ( - Version.parse("2.0.0-rc.1"), - Version.parse("1.22.0"), - Version.parse("1.19.3"), + "2.0.0-rc.1", + "1.22.0", + "1.19.3", LevelBump.MAJOR, True, "rc", - Version.parse("2.0.0-rc.2"), + "2.0.0-rc.2", ), ], ) def test_increment_version_no_major_on_zero( - latest_version, - latest_full_version, - latest_full_version_in_history, - level_bump, - prerelease, - prerelease_token, - expected_version, + latest_version: str, + latest_full_version: str, + latest_full_version_in_history: str, + level_bump: LevelBump, + prerelease: bool, + prerelease_token: str, + expected_version: str, ): actual = _increment_version( - latest_version=latest_version, - latest_full_version=latest_full_version, - latest_full_version_in_history=latest_full_version_in_history, + latest_version=Version.parse(latest_version), + latest_full_version=Version.parse(latest_full_version), + latest_full_version_in_history=Version.parse(latest_full_version_in_history), level_bump=level_bump, prerelease=prerelease, prerelease_token=prerelease_token, major_on_zero=False, + allow_zero_version=True, ) - assert actual == expected_version + assert expected_version == str(actual) From 36142a6d96446f4117220205fec8e6fe3d63ad42 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 11:41:05 -0500 Subject: [PATCH 110/167] test(scenario): add variation of `allow_zero_version` flag --- tests/scenario/test_next_version.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index 579cf28e9..04e9350cd 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -98,6 +98,7 @@ ), ) @pytest.mark.parametrize("major_on_zero", [True, False]) +@pytest.mark.parametrize("allow_zero_version", [True, False]) def test_algorithm_no_zero_dot_versions_angular( repo, file_in_repo, @@ -107,13 +108,14 @@ def test_algorithm_no_zero_dot_versions_angular( prerelease, expected_new_version, major_on_zero, + allow_zero_version, ): for commit_message in commit_messages: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) new_version = next_version( - repo, translator, commit_parser, prerelease, major_on_zero + repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) assert new_version == Version.parse( @@ -186,6 +188,7 @@ def test_algorithm_no_zero_dot_versions_angular( ), ) @pytest.mark.parametrize("major_on_zero", [True, False]) +@pytest.mark.parametrize("allow_zero_version", [True, False]) def test_algorithm_no_zero_dot_versions_emoji( repo, file_in_repo, @@ -195,13 +198,14 @@ def test_algorithm_no_zero_dot_versions_emoji( prerelease, expected_new_version, major_on_zero, + allow_zero_version, ): for commit_message in commit_messages: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) new_version = next_version( - repo, translator, commit_parser, prerelease, major_on_zero + repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) assert new_version == Version.parse( @@ -274,6 +278,7 @@ def test_algorithm_no_zero_dot_versions_emoji( ), ) @pytest.mark.parametrize("major_on_zero", [True, False]) +@pytest.mark.parametrize("allow_zero_version", [True, False]) def test_algorithm_no_zero_dot_versions_scipy( repo, file_in_repo, @@ -283,13 +288,14 @@ def test_algorithm_no_zero_dot_versions_scipy( prerelease, expected_new_version, major_on_zero, + allow_zero_version, ): for commit_message in commit_messages: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) new_version = next_version( - repo, translator, commit_parser, prerelease, major_on_zero + repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) assert new_version == Version.parse( @@ -362,6 +368,7 @@ def test_algorithm_no_zero_dot_versions_scipy( ), ) @pytest.mark.parametrize("major_on_zero", [True, False]) +@pytest.mark.parametrize("allow_zero_version", [True, False]) def test_algorithm_no_zero_dot_versions_tag( repo, file_in_repo, @@ -371,13 +378,14 @@ def test_algorithm_no_zero_dot_versions_tag( prerelease, expected_new_version, major_on_zero, + allow_zero_version, ): for commit_message in commit_messages: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) new_version = next_version( - repo, translator, commit_parser, prerelease, major_on_zero + repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) assert new_version == Version.parse( From e3bb4d3dc8aa5e14b11396f4e9fe1901f20ed4a3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 23 Dec 2023 22:47:22 -0500 Subject: [PATCH 111/167] test(fixtures): add config modifier fixture --- tests/fixtures/example_project.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 6d79ba402..129501909 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -37,6 +37,10 @@ ExProjectDir = Path + class SetFlagFn(Protocol): + def __call__(self, flag: bool) -> None: + ... + class UpdatePyprojectTomlFn(Protocol): def __call__(self, setting: str, value: Any) -> None: ... @@ -277,6 +281,24 @@ def _update_pyproject_toml(setting: str, value: Any) -> None: return _update_pyproject_toml +@pytest.fixture +def set_major_on_zero(update_pyproject_toml: UpdatePyprojectTomlFn) -> SetFlagFn: + """Turn on/off the major_on_zero setting.""" + def _set_major_on_zero(flag: bool) -> None: + update_pyproject_toml("tool.semantic_release.major_on_zero", flag) + + return _set_major_on_zero + + +@pytest.fixture +def set_allow_zero_version(update_pyproject_toml: UpdatePyprojectTomlFn) -> SetFlagFn: + """Turn on/off the allow_zero_version setting.""" + def _set_allow_zero_version(flag: bool) -> None: + update_pyproject_toml("tool.semantic_release.allow_zero_version", flag) + + return _set_allow_zero_version + + @pytest.fixture(scope="session") def use_angular_parser(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseParserFn: """Modify the configuration file to use the Angular parser.""" From a2b43e2759b334775edc9c65b0327b70808416d3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 21:40:31 -0500 Subject: [PATCH 112/167] test(next-version): refactor fixture references for maintainability --- tests/fixtures/scipy.py | 13 +- tests/scenario/test_next_version.py | 574 ++++++++++++++++++---------- 2 files changed, 387 insertions(+), 200 deletions(-) diff --git a/tests/fixtures/scipy.py b/tests/fixtures/scipy.py index 7bdfcd543..4efcbecda 100644 --- a/tests/fixtures/scipy.py +++ b/tests/fixtures/scipy.py @@ -71,6 +71,13 @@ def valid_scipy_commit(scipy_tag, subject, body_parts): return _make_scipy_commit(scipy_tag, subject, body_parts) +@pytest.fixture +def scipy_chore_commits(): + return [ + "DOC: Add a note to the documentation" + ] + + @pytest.fixture( params=xdist_sort_hack( [ @@ -80,7 +87,7 @@ def valid_scipy_commit(scipy_tag, subject, body_parts): ] ) ) -def scipy_commits_patch(request, subject): +def scipy_patch_commits(request, subject): return [ _make_scipy_commit(request.param, subject, body_parts) for body_parts in SCIPY_FORMATTED_COMMIT_BODY_PARTS @@ -96,7 +103,7 @@ def scipy_commits_patch(request, subject): ] ) ) -def scipy_commits_minor(request, subject, default_scipy_parser_options): +def scipy_minor_commits(request, subject, default_scipy_parser_options): patch_tags = [ k for k, v in default_scipy_parser_options.tag_to_level.items() @@ -126,7 +133,7 @@ def scipy_commits_minor(request, subject, default_scipy_parser_options): ] ) ) -def scipy_commits_major(request, subject, default_scipy_parser_options): +def scipy_major_commits(request, subject, default_scipy_parser_options): patch_tags = [ k for k, v in default_scipy_parser_options.tag_to_level.items() diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index 04e9350cd..f9eca3f13 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + import pytest # Limitation in pytest-lazy-fixture - see https://stackoverflow.com/a/69884019 @@ -18,8 +22,96 @@ TAG_COMMITS_MINOR, TAG_COMMITS_PATCH, ) +from tests.fixtures import ( + default_tag_parser, + repo_w_github_flow_w_feature_release_channel_tag_commits, + repo_with_no_tags_tag_commits, + repo_with_single_branch_and_prereleases_tag_commits, + repo_with_single_branch_tag_commits, + default_angular_parser, + default_emoji_parser, + repo_with_git_flow_angular_commits, + repo_with_git_flow_emoji_commits, + repo_with_git_flow_scipy_commits, + repo_with_git_flow_tag_commits, + repo_with_git_flow_and_release_channels_angular_commits, + repo_with_git_flow_and_release_channels_emoji_commits, + repo_with_git_flow_and_release_channels_scipy_commits, + repo_with_git_flow_and_release_channels_tag_commits, + default_scipy_parser, + repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_with_no_tags_angular_commits, + repo_with_single_branch_and_prereleases_angular_commits, + repo_with_single_branch_angular_commits, + scipy_chore_commits, + scipy_major_commits, + scipy_minor_commits, + scipy_patch_commits, +) from tests.util import add_text_to_file, xdist_sort_hack +if TYPE_CHECKING: + from git import Repo + + +@pytest.fixture +def angular_major_commits(): + return ANGULAR_COMMITS_MAJOR + +@pytest.fixture +def angular_minor_commits(): + return ANGULAR_COMMITS_MINOR + +@pytest.fixture +def angular_patch_commits(): + return ANGULAR_COMMITS_PATCH + + +@pytest.fixture +def angular_chore_commits() -> list[str]: + return ["chore: change dev tool configuration"] + + +@pytest.fixture +def emoji_major_commits(): + return EMOJI_COMMITS_MAJOR + + +@pytest.fixture +def emoji_minor_commits(): + return EMOJI_COMMITS_MINOR + + +@pytest.fixture +def emoji_patch_commits(): + return EMOJI_COMMITS_PATCH + + +@pytest.fixture +def emoji_chore_commits() -> list[str]: + return [":broom: change dev tool configuration"] + + +@pytest.fixture +def tag_patch_commits(): + return TAG_COMMITS_PATCH + + +@pytest.fixture +def tag_minor_commits(): + return TAG_COMMITS_MINOR + + +@pytest.fixture +def tag_major_commits(): + return TAG_COMMITS_MAJOR + + +@pytest.fixture +def tag_chore_commits() -> list[str]: + return [":broom: change dev tool configuration"] + + # TODO: it'd be nice to not hard-code the versions into # this testing @@ -51,46 +143,64 @@ # The last full release version was 1.1.1, so it's had a minor # prerelease ( - "repo_with_git_flow_angular_commits", - "default_angular_parser", + repo_with_git_flow_angular_commits.__name__, + default_angular_parser.__name__, VersionTranslator(prerelease_token="alpha"), ): [ *( (commits, True, "1.2.0-alpha.2") - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + ) ), # Models a merge of commits from the branch to the main branch, now # that prerelease=False - *((commits, False, "1.2.0") for commits in ([], ["uninteresting"])), - (ANGULAR_COMMITS_PATCH, False, "1.2.0"), - (ANGULAR_COMMITS_PATCH, True, "1.2.0-alpha.3"), - (ANGULAR_COMMITS_MINOR, False, "1.2.0"), - (ANGULAR_COMMITS_MINOR, True, "1.2.0-alpha.3"), - (ANGULAR_COMMITS_MAJOR, False, "2.0.0"), - (ANGULAR_COMMITS_MAJOR, True, "2.0.0-alpha.1"), + *( + (commits, False, "1.2.0") + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + ) + ), + (lazy_fixture(angular_patch_commits.__name__), False, "1.2.0"), + (lazy_fixture(angular_patch_commits.__name__), True, "1.2.0-alpha.3"), + (lazy_fixture(angular_minor_commits.__name__), False, "1.2.0"), + (lazy_fixture(angular_minor_commits.__name__), True, "1.2.0-alpha.3"), + (lazy_fixture(angular_major_commits.__name__), False, "2.0.0"), + (lazy_fixture(angular_major_commits.__name__), True, "2.0.0-alpha.1"), ], # Latest version for repo_with_git_flow_and_release_channels is # currently 1.1.0-alpha.3 # The last full release version was 1.0.0, so it's had a minor # prerelease ( - "repo_with_git_flow_and_release_channels_angular_commits", - "default_angular_parser", + repo_with_git_flow_and_release_channels_angular_commits.__name__, + default_angular_parser.__name__, VersionTranslator(prerelease_token="alpha"), ): [ *( (commits, True, "1.1.0-alpha.3") - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + ) ), # Models a merge of commits from the branch to the main branch, now # that prerelease=False - *((commits, False, "1.1.0") for commits in ([], ["uninteresting"])), - (ANGULAR_COMMITS_PATCH, False, "1.1.0"), - (ANGULAR_COMMITS_PATCH, True, "1.1.0-alpha.4"), - (ANGULAR_COMMITS_MINOR, False, "1.1.0"), - (ANGULAR_COMMITS_MINOR, True, "1.1.0-alpha.4"), - (ANGULAR_COMMITS_MAJOR, False, "2.0.0"), - (ANGULAR_COMMITS_MAJOR, True, "2.0.0-alpha.1"), + *( + (commits, False, "1.1.0") + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + ) + ), + (lazy_fixture(angular_patch_commits.__name__), False, "1.1.0"), + (lazy_fixture(angular_patch_commits.__name__), True, "1.1.0-alpha.4"), + (lazy_fixture(angular_minor_commits.__name__), False, "1.1.0"), + (lazy_fixture(angular_minor_commits.__name__), True, "1.1.0-alpha.4"), + (lazy_fixture(angular_major_commits.__name__), False, "2.0.0"), + (lazy_fixture(angular_major_commits.__name__), True, "2.0.0-alpha.1"), ], }.items() for (commit_messages, prerelease, expected_new_version) in values @@ -110,17 +220,18 @@ def test_algorithm_no_zero_dot_versions_angular( major_on_zero, allow_zero_version, ): - for commit_message in commit_messages: + # Setup + for commit_message in commit_messages or []: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) + # Action new_version = next_version( repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) - assert new_version == Version.parse( - expected_new_version, prerelease_token=translator.prerelease_token - ) + # Verify + assert expected_new_version == str(new_version) @pytest.mark.parametrize( @@ -141,46 +252,64 @@ def test_algorithm_no_zero_dot_versions_angular( # The last full release version was 1.1.1, so it's had a minor # prerelease ( - "repo_with_git_flow_emoji_commits", - "default_emoji_parser", + repo_with_git_flow_emoji_commits.__name__, + default_emoji_parser.__name__, VersionTranslator(prerelease_token="alpha"), ): [ *( (commits, True, "1.2.0-alpha.2") - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(emoji_chore_commits.__name__), + ) ), # Models a merge of commits from the branch to the main branch, now # that prerelease=False - *((commits, False, "1.2.0") for commits in ([], ["uninteresting"])), - (EMOJI_COMMITS_PATCH, False, "1.2.0"), - (EMOJI_COMMITS_PATCH, True, "1.2.0-alpha.3"), - (EMOJI_COMMITS_MINOR, False, "1.2.0"), - (EMOJI_COMMITS_MINOR, True, "1.2.0-alpha.3"), - (EMOJI_COMMITS_MAJOR, False, "2.0.0"), - (EMOJI_COMMITS_MAJOR, True, "2.0.0-alpha.1"), + *( + (commits, False, "1.2.0") + for commits in ( + None, + lazy_fixture(emoji_chore_commits.__name__), + ) + ), + (lazy_fixture(emoji_patch_commits.__name__), False, "1.2.0"), + (lazy_fixture(emoji_patch_commits.__name__), True, "1.2.0-alpha.3"), + (lazy_fixture(emoji_minor_commits.__name__), False, "1.2.0"), + (lazy_fixture(emoji_minor_commits.__name__), True, "1.2.0-alpha.3"), + (lazy_fixture(emoji_major_commits.__name__), False, "2.0.0"), + (lazy_fixture(emoji_major_commits.__name__), True, "2.0.0-alpha.1"), ], # Latest version for repo_with_git_flow_and_release_channels is # currently 1.1.0-alpha.3 # The last full release version was 1.0.0, so it's had a minor # prerelease ( - "repo_with_git_flow_and_release_channels_emoji_commits", - "default_emoji_parser", + repo_with_git_flow_and_release_channels_emoji_commits.__name__, + default_emoji_parser.__name__, VersionTranslator(prerelease_token="alpha"), ): [ *( (commits, True, "1.1.0-alpha.3") - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(emoji_chore_commits.__name__), + ) ), # Models a merge of commits from the branch to the main branch, now # that prerelease=False - *((commits, False, "1.1.0") for commits in ([], ["uninteresting"])), - (EMOJI_COMMITS_PATCH, False, "1.1.0"), - (EMOJI_COMMITS_PATCH, True, "1.1.0-alpha.4"), - (EMOJI_COMMITS_MINOR, False, "1.1.0"), - (EMOJI_COMMITS_MINOR, True, "1.1.0-alpha.4"), - (EMOJI_COMMITS_MAJOR, False, "2.0.0"), - (EMOJI_COMMITS_MAJOR, True, "2.0.0-alpha.1"), + *( + (commits, False, "1.1.0") + for commits in ( + None, + lazy_fixture(emoji_chore_commits.__name__), + ) + ), + (lazy_fixture(emoji_patch_commits.__name__), False, "1.1.0"), + (lazy_fixture(emoji_patch_commits.__name__), True, "1.1.0-alpha.4"), + (lazy_fixture(emoji_minor_commits.__name__), False, "1.1.0"), + (lazy_fixture(emoji_minor_commits.__name__), True, "1.1.0-alpha.4"), + (lazy_fixture(emoji_major_commits.__name__), False, "2.0.0"), + (lazy_fixture(emoji_major_commits.__name__), True, "2.0.0-alpha.1"), ], }.items() for (commit_messages, prerelease, expected_new_version) in values @@ -200,17 +329,18 @@ def test_algorithm_no_zero_dot_versions_emoji( major_on_zero, allow_zero_version, ): - for commit_message in commit_messages: + # Setup + for commit_message in commit_messages or []: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) + # Action new_version = next_version( repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) - assert new_version == Version.parse( - expected_new_version, prerelease_token=translator.prerelease_token - ) + # Verify + assert expected_new_version == str(new_version) @pytest.mark.parametrize( @@ -231,46 +361,64 @@ def test_algorithm_no_zero_dot_versions_emoji( # The last full release version was 1.1.1, so it's had a minor # prerelease ( - "repo_with_git_flow_scipy_commits", - "default_scipy_parser", + repo_with_git_flow_scipy_commits.__name__, + default_scipy_parser.__name__, VersionTranslator(prerelease_token="alpha"), ): [ *( (commits, True, "1.2.0-alpha.2") - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(scipy_chore_commits.__name__), + ) ), # Models a merge of commits from the branch to the main branch, now # that prerelease=False - *((commits, False, "1.2.0") for commits in ([], ["uninteresting"])), - (lazy_fixture("scipy_commits_patch"), False, "1.2.0"), - (lazy_fixture("scipy_commits_patch"), True, "1.2.0-alpha.3"), - (lazy_fixture("scipy_commits_minor"), False, "1.2.0"), - (lazy_fixture("scipy_commits_minor"), True, "1.2.0-alpha.3"), - (lazy_fixture("scipy_commits_major"), False, "2.0.0"), - (lazy_fixture("scipy_commits_major"), True, "2.0.0-alpha.1"), + *( + (commits, False, "1.2.0") + for commits in ( + None, + lazy_fixture(scipy_chore_commits.__name__), + ) + ), + (lazy_fixture(scipy_patch_commits.__name__), False, "1.2.0"), + (lazy_fixture(scipy_patch_commits.__name__), True, "1.2.0-alpha.3"), + (lazy_fixture(scipy_minor_commits.__name__), False, "1.2.0"), + (lazy_fixture(scipy_minor_commits.__name__), True, "1.2.0-alpha.3"), + (lazy_fixture(scipy_major_commits.__name__), False, "2.0.0"), + (lazy_fixture(scipy_major_commits.__name__), True, "2.0.0-alpha.1"), ], # Latest version for repo_with_git_flow_and_release_channels is # currently 1.1.0-alpha.3 # The last full release version was 1.0.0, so it's had a minor # prerelease ( - "repo_with_git_flow_and_release_channels_scipy_commits", - "default_scipy_parser", + repo_with_git_flow_and_release_channels_scipy_commits.__name__, + default_scipy_parser.__name__, VersionTranslator(prerelease_token="alpha"), ): [ *( (commits, True, "1.1.0-alpha.3") - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(scipy_chore_commits.__name__), + ) ), # Models a merge of commits from the branch to the main branch, now # that prerelease=False - *((commits, False, "1.1.0") for commits in ([], ["uninteresting"])), - (lazy_fixture("scipy_commits_patch"), False, "1.1.0"), - (lazy_fixture("scipy_commits_patch"), True, "1.1.0-alpha.4"), - (lazy_fixture("scipy_commits_minor"), False, "1.1.0"), - (lazy_fixture("scipy_commits_minor"), True, "1.1.0-alpha.4"), - (lazy_fixture("scipy_commits_major"), False, "2.0.0"), - (lazy_fixture("scipy_commits_major"), True, "2.0.0-alpha.1"), + *( + (commits, False, "1.1.0") + for commits in ( + None, + lazy_fixture(scipy_chore_commits.__name__), + ) + ), + (lazy_fixture(scipy_patch_commits.__name__), False, "1.1.0"), + (lazy_fixture(scipy_patch_commits.__name__), True, "1.1.0-alpha.4"), + (lazy_fixture(scipy_minor_commits.__name__), False, "1.1.0"), + (lazy_fixture(scipy_minor_commits.__name__), True, "1.1.0-alpha.4"), + (lazy_fixture(scipy_major_commits.__name__), False, "2.0.0"), + (lazy_fixture(scipy_major_commits.__name__), True, "2.0.0-alpha.1"), ], }.items() for (commit_messages, prerelease, expected_new_version) in values @@ -290,17 +438,18 @@ def test_algorithm_no_zero_dot_versions_scipy( major_on_zero, allow_zero_version, ): - for commit_message in commit_messages: + # Setup + for commit_message in commit_messages or []: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) + # Action new_version = next_version( repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) - assert new_version == Version.parse( - expected_new_version, prerelease_token=translator.prerelease_token - ) + # Verify + assert expected_new_version == str(new_version) @pytest.mark.parametrize( @@ -321,46 +470,64 @@ def test_algorithm_no_zero_dot_versions_scipy( # The last full release version was 1.1.1, so it's had a minor # prerelease ( - "repo_with_git_flow_tag_commits", - "default_tag_parser", + repo_with_git_flow_tag_commits.__name__, + default_tag_parser.__name__, VersionTranslator(prerelease_token="alpha"), ): [ *( (commits, True, "1.2.0-alpha.2") - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(tag_chore_commits.__name__), + ) ), # Models a merge of commits from the branch to the main branch, now # that prerelease=False - *((commits, False, "1.2.0") for commits in ([], ["uninteresting"])), - (TAG_COMMITS_PATCH, False, "1.2.0"), - (TAG_COMMITS_PATCH, True, "1.2.0-alpha.3"), - (TAG_COMMITS_MINOR, False, "1.2.0"), - (TAG_COMMITS_MINOR, True, "1.2.0-alpha.3"), - (TAG_COMMITS_MAJOR, False, "2.0.0"), - (TAG_COMMITS_MAJOR, True, "2.0.0-alpha.1"), + *( + (commits, False, "1.2.0") + for commits in ( + None, + lazy_fixture(tag_chore_commits.__name__), + ) + ), + (lazy_fixture(tag_patch_commits.__name__), False, "1.2.0"), + (lazy_fixture(tag_patch_commits.__name__), True, "1.2.0-alpha.3"), + (lazy_fixture(tag_minor_commits.__name__), False, "1.2.0"), + (lazy_fixture(tag_minor_commits.__name__), True, "1.2.0-alpha.3"), + (lazy_fixture(tag_major_commits.__name__), False, "2.0.0"), + (lazy_fixture(tag_major_commits.__name__), True, "2.0.0-alpha.1"), ], # Latest version for repo_with_git_flow_and_release_channels is # currently 1.1.0-alpha.3 # The last full release version was 1.0.0, so it's had a minor # prerelease ( - "repo_with_git_flow_and_release_channels_tag_commits", - "default_tag_parser", + repo_with_git_flow_and_release_channels_tag_commits.__name__, + default_tag_parser.__name__, VersionTranslator(prerelease_token="alpha"), ): [ *( (commits, True, "1.1.0-alpha.3") - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(tag_chore_commits.__name__), + ) ), # Models a merge of commits from the branch to the main branch, now # that prerelease=False - *((commits, False, "1.1.0") for commits in ([], ["uninteresting"])), - (TAG_COMMITS_PATCH, False, "1.1.0"), - (TAG_COMMITS_PATCH, True, "1.1.0-alpha.4"), - (TAG_COMMITS_MINOR, False, "1.1.0"), - (TAG_COMMITS_MINOR, True, "1.1.0-alpha.4"), - (TAG_COMMITS_MAJOR, False, "2.0.0"), - (TAG_COMMITS_MAJOR, True, "2.0.0-alpha.1"), + *( + (commits, False, "1.1.0") + for commits in ( + None, + lazy_fixture(tag_chore_commits.__name__), + ) + ), + (lazy_fixture(tag_patch_commits.__name__), False, "1.1.0"), + (lazy_fixture(tag_patch_commits.__name__), True, "1.1.0-alpha.4"), + (lazy_fixture(tag_minor_commits.__name__), False, "1.1.0"), + (lazy_fixture(tag_minor_commits.__name__), True, "1.1.0-alpha.4"), + (lazy_fixture(tag_major_commits.__name__), False, "2.0.0"), + (lazy_fixture(tag_major_commits.__name__), True, "2.0.0-alpha.1"), ], }.items() for (commit_messages, prerelease, expected_new_version) in values @@ -380,17 +547,18 @@ def test_algorithm_no_zero_dot_versions_tag( major_on_zero, allow_zero_version, ): - for commit_message in commit_messages: + # Setup + for commit_message in commit_messages or []: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) + # Action new_version = next_version( repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) - assert new_version == Version.parse( - expected_new_version, prerelease_token=translator.prerelease_token - ) + # Verify + assert expected_new_version == str(new_version) ##### @@ -416,125 +584,131 @@ def test_algorithm_no_zero_dot_versions_tag( # Latest version for repo_with_no_tags is currently 0.0.0 (default) # It's biggest change type is minor, so the next version should be 0.1.0 ( - "repo_with_no_tags_angular_commits", - "default_angular_parser", + repo_with_no_tags_angular_commits.__name__, + default_angular_parser.__name__, VersionTranslator(), ): [ *( (commits, False, major_on_zero, "0.1.0") for major_on_zero in (True, False) for commits in ( - [], - ["uninteresting"], - ANGULAR_COMMITS_PATCH, - ANGULAR_COMMITS_MINOR, + None, + lazy_fixture(angular_chore_commits.__name__), + lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(angular_minor_commits.__name__), ) ), - (ANGULAR_COMMITS_MAJOR, False, False, "0.1.0"), - (ANGULAR_COMMITS_MAJOR, False, True, "1.0.0"), + (lazy_fixture(angular_major_commits.__name__), False, False, "0.1.0"), + (lazy_fixture(angular_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch is currently 0.1.1 # Note repo_with_single_branch isn't modelled with prereleases ( - "repo_with_single_branch_angular_commits", - "default_angular_parser", + repo_with_single_branch_angular_commits.__name__, + default_angular_parser.__name__, VersionTranslator(), ): [ *( (commits, False, major_on_zero, "0.1.1") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + ) ), *( - (ANGULAR_COMMITS_PATCH, False, major_on_zero, "0.1.2") + (lazy_fixture(angular_patch_commits.__name__), False, major_on_zero, "0.1.2") for major_on_zero in (True, False) ), *( - (ANGULAR_COMMITS_MINOR, False, major_on_zero, "0.2.0") + (lazy_fixture(angular_minor_commits.__name__), False, major_on_zero, "0.2.0") for major_on_zero in (True, False) ), - (ANGULAR_COMMITS_MAJOR, False, False, "0.2.0"), - (ANGULAR_COMMITS_MAJOR, False, True, "1.0.0"), + (lazy_fixture(angular_major_commits.__name__), False, False, "0.2.0"), + (lazy_fixture(angular_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch_and_prereleases is # currently 0.2.0 ( - "repo_with_single_branch_and_prereleases_angular_commits", - "default_angular_parser", + repo_with_single_branch_and_prereleases_angular_commits.__name__, + default_angular_parser.__name__, VersionTranslator(), ): [ *( (commits, prerelease, major_on_zero, "0.2.0") for prerelease in (True, False) for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__) + ) ), *( - (ANGULAR_COMMITS_PATCH, False, major_on_zero, "0.2.1") + (lazy_fixture(angular_patch_commits.__name__), False, major_on_zero, "0.2.1") for major_on_zero in (True, False) ), *( - (ANGULAR_COMMITS_PATCH, True, major_on_zero, "0.2.1-rc.1") + (lazy_fixture(angular_patch_commits.__name__), True, major_on_zero, "0.2.1-rc.1") for major_on_zero in (True, False) ), *( - (ANGULAR_COMMITS_MINOR, False, major_on_zero, "0.3.0") + (lazy_fixture(angular_minor_commits.__name__), False, major_on_zero, "0.3.0") for major_on_zero in (True, False) ), *( - (ANGULAR_COMMITS_MINOR, True, major_on_zero, "0.3.0-rc.1") + (lazy_fixture(angular_minor_commits.__name__), True, major_on_zero, "0.3.0-rc.1") for major_on_zero in (True, False) ), - (ANGULAR_COMMITS_MAJOR, False, True, "1.0.0"), - (ANGULAR_COMMITS_MAJOR, True, True, "1.0.0-rc.1"), - (ANGULAR_COMMITS_MAJOR, False, False, "0.3.0"), - (ANGULAR_COMMITS_MAJOR, True, False, "0.3.0-rc.1"), + (lazy_fixture(angular_major_commits.__name__), False, True, "1.0.0"), + (lazy_fixture(angular_major_commits.__name__), True, True, "1.0.0-rc.1"), + (lazy_fixture(angular_major_commits.__name__), False, False, "0.3.0"), + (lazy_fixture(angular_major_commits.__name__), True, False, "0.3.0-rc.1"), ], # Latest version for repo_with_main_and_feature_branches is currently # 0.3.0-rc.1. # The last full release version was 0.2.0, so it's had a minor # prerelease ( - "repo_w_github_flow_w_feature_release_channel_angular_commits", - "default_angular_parser", + repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, + default_angular_parser.__name__, VersionTranslator(prerelease_token="beta"), ): [ *( (commits, True, major_on_zero, "0.3.0-beta.1") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(angular_chore_commits.__name__)) ), # Models a merge of commits from the branch to the main branch, now # that prerelease=False *( (commits, False, major_on_zero, "0.3.0") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(angular_chore_commits.__name__)) ), *( - (ANGULAR_COMMITS_PATCH, False, major_on_zero, "0.3.0") + (lazy_fixture(angular_patch_commits.__name__), False, major_on_zero, "0.3.0") for major_on_zero in (True, False) ), *( - (ANGULAR_COMMITS_PATCH, True, major_on_zero, "0.3.0-beta.2") + (lazy_fixture(angular_patch_commits.__name__), True, major_on_zero, "0.3.0-beta.2") for major_on_zero in (True, False) ), *( - (ANGULAR_COMMITS_MINOR, False, major_on_zero, "0.3.0") + (lazy_fixture(angular_minor_commits.__name__), False, major_on_zero, "0.3.0") for major_on_zero in (True, False) ), *( - (ANGULAR_COMMITS_MINOR, True, major_on_zero, "0.3.0-beta.2") + (lazy_fixture(angular_minor_commits.__name__), True, major_on_zero, "0.3.0-beta.2") for major_on_zero in (True, False) ), - (ANGULAR_COMMITS_MAJOR, False, True, "1.0.0"), - (ANGULAR_COMMITS_MAJOR, True, True, "1.0.0-beta.1"), - (ANGULAR_COMMITS_MAJOR, False, False, "0.3.0"), + (lazy_fixture(angular_major_commits.__name__), False, True, "1.0.0"), + (lazy_fixture(angular_major_commits.__name__), True, True, "1.0.0-beta.1"), + (lazy_fixture(angular_major_commits.__name__), False, False, "0.3.0"), # Note - since breaking changes are absorbed into the minor digit # with major_on_zero = False, and that's already been incremented # since the last full release, the breaking change here will only # trigger a prerelease revision - (ANGULAR_COMMITS_MAJOR, True, False, "0.3.0-beta.2"), + (lazy_fixture(angular_major_commits.__name__), True, False, "0.3.0-beta.2"), ], }.items() for ( @@ -555,18 +729,20 @@ def test_algorithm_with_zero_dot_versions_angular( prerelease, expected_new_version, major_on_zero, + allow_zero_version, ): - for commit_message in commit_messages: + # Setup + for commit_message in commit_messages or []: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) + # Action new_version = next_version( - repo, translator, commit_parser, prerelease, major_on_zero + repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) - assert new_version == Version.parse( - expected_new_version, prerelease_token=translator.prerelease_token - ) + # Verify + assert expected_new_version == str(new_version) @pytest.mark.parametrize( @@ -597,12 +773,12 @@ def test_algorithm_with_zero_dot_versions_angular( for commits in ( [], ["uninteresting"], - EMOJI_COMMITS_PATCH, - EMOJI_COMMITS_MINOR, + lazy_fixture(emoji_patch_commits.__name__), + lazy_fixture(emoji_minor_commits.__name__), ) ), - (EMOJI_COMMITS_MAJOR, False, False, "0.1.0"), - (EMOJI_COMMITS_MAJOR, False, True, "1.0.0"), + (lazy_fixture(emoji_major_commits.__name__), False, False, "0.1.0"), + (lazy_fixture(emoji_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch is currently 0.1.1 # Note repo_with_single_branch isn't modelled with prereleases @@ -617,15 +793,15 @@ def test_algorithm_with_zero_dot_versions_angular( for commits in ([], ["uninteresting"]) ), *( - (EMOJI_COMMITS_PATCH, False, major_on_zero, "0.1.2") + (lazy_fixture(emoji_patch_commits.__name__), False, major_on_zero, "0.1.2") for major_on_zero in (True, False) ), *( - (EMOJI_COMMITS_MINOR, False, major_on_zero, "0.2.0") + (lazy_fixture(emoji_minor_commits.__name__), False, major_on_zero, "0.2.0") for major_on_zero in (True, False) ), - (EMOJI_COMMITS_MAJOR, False, False, "0.2.0"), - (EMOJI_COMMITS_MAJOR, False, True, "1.0.0"), + (lazy_fixture(emoji_major_commits.__name__), False, False, "0.2.0"), + (lazy_fixture(emoji_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch_and_prereleases is # currently 0.2.0 @@ -641,25 +817,25 @@ def test_algorithm_with_zero_dot_versions_angular( for commits in ([], ["uninteresting"]) ), *( - (EMOJI_COMMITS_PATCH, False, major_on_zero, "0.2.1") + (lazy_fixture(emoji_patch_commits.__name__), False, major_on_zero, "0.2.1") for major_on_zero in (True, False) ), *( - (EMOJI_COMMITS_PATCH, True, major_on_zero, "0.2.1-rc.1") + (lazy_fixture(emoji_patch_commits.__name__), True, major_on_zero, "0.2.1-rc.1") for major_on_zero in (True, False) ), *( - (EMOJI_COMMITS_MINOR, False, major_on_zero, "0.3.0") + (lazy_fixture(emoji_minor_commits.__name__), False, major_on_zero, "0.3.0") for major_on_zero in (True, False) ), *( - (EMOJI_COMMITS_MINOR, True, major_on_zero, "0.3.0-rc.1") + (lazy_fixture(emoji_minor_commits.__name__), True, major_on_zero, "0.3.0-rc.1") for major_on_zero in (True, False) ), - (EMOJI_COMMITS_MAJOR, False, True, "1.0.0"), - (EMOJI_COMMITS_MAJOR, True, True, "1.0.0-rc.1"), - (EMOJI_COMMITS_MAJOR, False, False, "0.3.0"), - (EMOJI_COMMITS_MAJOR, True, False, "0.3.0-rc.1"), + (lazy_fixture(emoji_major_commits.__name__), False, True, "1.0.0"), + (lazy_fixture(emoji_major_commits.__name__), True, True, "1.0.0-rc.1"), + (lazy_fixture(emoji_major_commits.__name__), False, False, "0.3.0"), + (lazy_fixture(emoji_major_commits.__name__), True, False, "0.3.0-rc.1"), ], # Latest version for repo_with_main_and_feature_branches is currently # 0.3.0-beta.1. @@ -683,29 +859,29 @@ def test_algorithm_with_zero_dot_versions_angular( for commits in ([], ["uninteresting"]) ), *( - (EMOJI_COMMITS_PATCH, False, major_on_zero, "0.3.0") + (lazy_fixture(emoji_patch_commits.__name__), False, major_on_zero, "0.3.0") for major_on_zero in (True, False) ), *( - (EMOJI_COMMITS_PATCH, True, major_on_zero, "0.3.0-beta.2") + (lazy_fixture(emoji_patch_commits.__name__), True, major_on_zero, "0.3.0-beta.2") for major_on_zero in (True, False) ), *( - (EMOJI_COMMITS_MINOR, False, major_on_zero, "0.3.0") + (lazy_fixture(emoji_minor_commits.__name__), False, major_on_zero, "0.3.0") for major_on_zero in (True, False) ), *( - (EMOJI_COMMITS_MINOR, True, major_on_zero, "0.3.0-beta.2") + (lazy_fixture(emoji_minor_commits.__name__), True, major_on_zero, "0.3.0-beta.2") for major_on_zero in (True, False) ), - (EMOJI_COMMITS_MAJOR, False, True, "1.0.0"), - (EMOJI_COMMITS_MAJOR, True, True, "1.0.0-beta.1"), - (EMOJI_COMMITS_MAJOR, False, False, "0.3.0"), + (lazy_fixture(emoji_major_commits.__name__), False, True, "1.0.0"), + (lazy_fixture(emoji_major_commits.__name__), True, True, "1.0.0-beta.1"), + (lazy_fixture(emoji_major_commits.__name__), False, False, "0.3.0"), # Note - since breaking changes are absorbed into the minor digit # with major_on_zero = False, and that's already been incremented # since the last full release, the breaking change here will only # trigger a prerelease revision - (EMOJI_COMMITS_MAJOR, True, False, "0.3.0-beta.2"), + (lazy_fixture(emoji_major_commits.__name__), True, False, "0.3.0-beta.2"), ], }.items() for ( @@ -726,18 +902,20 @@ def test_algorithm_with_zero_dot_versions_emoji( prerelease, expected_new_version, major_on_zero, + allow_zero_version, ): - for commit_message in commit_messages: + # Setup + for commit_message in commit_messages or []: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) + # Action new_version = next_version( - repo, translator, commit_parser, prerelease, major_on_zero + repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) - assert new_version == Version.parse( - expected_new_version, prerelease_token=translator.prerelease_token - ) + # Verify + assert expected_new_version == str(new_version) @pytest.mark.parametrize( @@ -768,12 +946,12 @@ def test_algorithm_with_zero_dot_versions_emoji( for commits in ( [], ["uninteresting"], - lazy_fixture("scipy_commits_patch"), - lazy_fixture("scipy_commits_minor"), + lazy_fixture(scipy_patch_commits.__name__), + lazy_fixture(scipy_minor_commits.__name__), ) ), - (lazy_fixture("scipy_commits_major"), False, False, "0.1.0"), - (lazy_fixture("scipy_commits_major"), False, True, "1.0.0"), + (lazy_fixture(scipy_major_commits.__name__), False, False, "0.1.0"), + (lazy_fixture(scipy_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch is currently 0.1.1 # Note repo_with_single_branch isn't modelled with prereleases @@ -789,7 +967,7 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_patch"), + lazy_fixture(scipy_patch_commits.__name__), False, major_on_zero, "0.1.2", @@ -798,15 +976,15 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_minor"), + lazy_fixture(scipy_minor_commits.__name__), False, major_on_zero, "0.2.0", ) for major_on_zero in (True, False) ), - (lazy_fixture("scipy_commits_major"), False, False, "0.2.0"), - (lazy_fixture("scipy_commits_major"), False, True, "1.0.0"), + (lazy_fixture(scipy_major_commits.__name__), False, False, "0.2.0"), + (lazy_fixture(scipy_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch_and_prereleases is # currently 0.2.0 @@ -823,7 +1001,7 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_patch"), + lazy_fixture(scipy_patch_commits.__name__), False, major_on_zero, "0.2.1", @@ -832,7 +1010,7 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_patch"), + lazy_fixture(scipy_patch_commits.__name__), True, major_on_zero, "0.2.1-rc.1", @@ -841,7 +1019,7 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_minor"), + lazy_fixture(scipy_minor_commits.__name__), False, major_on_zero, "0.3.0", @@ -850,17 +1028,17 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_minor"), + lazy_fixture(scipy_minor_commits.__name__), True, major_on_zero, "0.3.0-rc.1", ) for major_on_zero in (True, False) ), - (lazy_fixture("scipy_commits_major"), False, True, "1.0.0"), - (lazy_fixture("scipy_commits_major"), True, True, "1.0.0-rc.1"), - (lazy_fixture("scipy_commits_major"), False, False, "0.3.0"), - (lazy_fixture("scipy_commits_major"), True, False, "0.3.0-rc.1"), + (lazy_fixture(scipy_major_commits.__name__), False, True, "1.0.0"), + (lazy_fixture(scipy_major_commits.__name__), True, True, "1.0.0-rc.1"), + (lazy_fixture(scipy_major_commits.__name__), False, False, "0.3.0"), + (lazy_fixture(scipy_major_commits.__name__), True, False, "0.3.0-rc.1"), ], # Latest version for repo_with_main_and_feature_branches is currently # 0.3.0-rc.1. @@ -885,7 +1063,7 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_patch"), + lazy_fixture(scipy_patch_commits.__name__), False, major_on_zero, "0.3.0", @@ -894,7 +1072,7 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_patch"), + lazy_fixture(scipy_patch_commits.__name__), True, major_on_zero, "0.3.0-beta.2", @@ -903,7 +1081,7 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_minor"), + lazy_fixture(scipy_minor_commits.__name__), False, major_on_zero, "0.3.0", @@ -912,21 +1090,21 @@ def test_algorithm_with_zero_dot_versions_emoji( ), *( ( - lazy_fixture("scipy_commits_minor"), + lazy_fixture(scipy_minor_commits.__name__), True, major_on_zero, "0.3.0-beta.2", ) for major_on_zero in (True, False) ), - (lazy_fixture("scipy_commits_major"), False, True, "1.0.0"), - (lazy_fixture("scipy_commits_major"), True, True, "1.0.0-beta.1"), - (lazy_fixture("scipy_commits_major"), False, False, "0.3.0"), + (lazy_fixture(scipy_major_commits.__name__), False, True, "1.0.0"), + (lazy_fixture(scipy_major_commits.__name__), True, True, "1.0.0-beta.1"), + (lazy_fixture(scipy_major_commits.__name__), False, False, "0.3.0"), # Note - since breaking changes are absorbed into the minor digit # with major_on_zero = False, and that's already been incremented # since the last full release, the breaking change here will only # trigger a prerelease revision - (lazy_fixture("scipy_commits_major"), True, False, "0.3.0-beta.2"), + (lazy_fixture(scipy_major_commits.__name__), True, False, "0.3.0-beta.2"), ], }.items() for ( @@ -947,18 +1125,20 @@ def test_algorithm_with_zero_dot_versions_scipy( prerelease, expected_new_version, major_on_zero, + allow_zero_version, ): - for commit_message in commit_messages: + # Setup + for commit_message in commit_messages or []: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) + # Action new_version = next_version( - repo, translator, commit_parser, prerelease, major_on_zero + repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) - assert new_version == Version.parse( - expected_new_version, prerelease_token=translator.prerelease_token - ) + # Verify + assert expected_new_version == str(new_version) @pytest.mark.parametrize( From 7ed5fe504234acbfed72dd5fe4e76381475d4cce Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 21:41:50 -0500 Subject: [PATCH 113/167] test(next-version): add new test case to ensure minimum version determinations --- tests/scenario/test_next_version.py | 364 ++++++++++++++++++++++++++-- 1 file changed, 344 insertions(+), 20 deletions(-) diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index f9eca3f13..9be1237ba 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -1252,45 +1252,363 @@ def test_algorithm_with_zero_dot_versions_scipy( *( (commits, False, major_on_zero, "0.3.0") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + +@pytest.mark.parametrize( + str.join(" ,", [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ]), + xdist_sort_hack( + [ + ( + lazy_fixture(repo_with_no_tags_tag_commits.__name__), + lazy_fixture(parser_fixture_name), + translator[0], + commit_messages, + prerelease, + major_on_zero, + allow_zero_version, + expected_new_version, + ) + for translator, values in { + # Latest version for repo_with_no_tags is currently 0.0.0 (default) + # It's biggest change type is minor, so the next version should be 0.1.0 + (VersionTranslator(),): [ + *( + # when prerelease is False, major_on_zero is True & False, & allow_zero_version is True + # the version should be 0.0.0, when no distintive changes have been made since the + # start of the project + (commits, parser, prerelease, major_on_zero, True, "0.0.0") + for prerelease in (True, False) + for major_on_zero in (True, False) + for commits, parser in ( + # No commits added, so base is just initial commit at 0.0.0 + (None, default_tag_parser.__name__), + # Chore like commits also don't trigger a version bump so it stays 0.0.0 + ( + lazy_fixture(angular_chore_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(emoji_chore_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(scipy_chore_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(tag_chore_commits.__name__), + default_tag_parser.__name__, + ), + ) ), *( - (TAG_COMMITS_PATCH, False, major_on_zero, "0.3.0") + (commits, parser, True, major_on_zero, True, "0.0.1-rc.1") for major_on_zero in (True, False) + for commits, parser in ( + # when prerelease is True & allow_zero_version is True, the version should be + # a patch bump as a prerelease version, because of the patch level commits + # major_on_zero is irrelevant here as we are only applying patch commits + ( + lazy_fixture(angular_patch_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(emoji_patch_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(scipy_patch_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(tag_patch_commits.__name__), + default_tag_parser.__name__, + ), + ) ), *( - (TAG_COMMITS_PATCH, True, major_on_zero, "0.3.0-beta.2") + (commits, parser, False, major_on_zero, True, "0.0.1") for major_on_zero in (True, False) + for commits, parser in ( + # when prerelease is False, & allow_zero_version is True, the version should be + # a patch bump because of the patch commits added + # major_on_zero is irrelevant here as we are only applying patch commits + ( + lazy_fixture(angular_patch_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(emoji_patch_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(scipy_patch_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(tag_patch_commits.__name__), + default_tag_parser.__name__, + ), + ) ), *( - (TAG_COMMITS_MINOR, False, major_on_zero, "0.3.0") + (commits, parser, True, False, True, "0.1.0-rc.1") + for commits, parser in ( + # when prerelease is False, & major_on_zero is False, the version should be + # a minor bump because of the minor commits added + ( + lazy_fixture(angular_minor_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(emoji_minor_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(scipy_minor_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(tag_minor_commits.__name__), + default_tag_parser.__name__, + ), + # Given the major_on_zero is False and the version is starting at 0.0.0, + # the major level commits are limited to only causing a minor level bump + ( + lazy_fixture(angular_major_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(emoji_major_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(scipy_major_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(tag_major_commits.__name__), + default_tag_parser.__name__, + ), + ) + ), + *( + (commits, parser, False, False, True, "0.1.0") + for commits, parser in ( + # when prerelease is False, + # major_on_zero is False, & allow_zero_version is True + # the version should be a minor bump of 0.0.0 + # because of the minor commits added and zero version is allowed + ( + lazy_fixture(angular_minor_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(emoji_minor_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(scipy_minor_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(tag_minor_commits.__name__), + default_tag_parser.__name__, + ), + # Given the major_on_zero is False and the version is starting at 0.0.0, + # the major level commits are limited to only causing a minor level bump + ( + lazy_fixture(angular_major_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(emoji_major_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(scipy_major_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(tag_major_commits.__name__), + default_tag_parser.__name__, + ), + ) + ), + *( + # when prerelease is True, & allow_zero_version is False, the version should be + # a prerelease version 1.0.0-rc.1, across the board when any valuable change + # is made because of the allow_zero_version is False, major_on_zero is ignored + # when allow_zero_version is False (but we still test it) + (commits, parser, True, major_on_zero, False, "1.0.0-rc.1") for major_on_zero in (True, False) + for commits, parser in ( + # parser doesn't matter here as long as it detects a NO_RELEASE on Initial Commit + (None, default_tag_parser.__name__), + ( + lazy_fixture(angular_chore_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(angular_patch_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(angular_minor_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(angular_major_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(emoji_chore_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(emoji_patch_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(emoji_minor_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(emoji_major_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(scipy_chore_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(scipy_patch_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(scipy_minor_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(scipy_major_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(tag_chore_commits.__name__), + default_tag_parser.__name__, + ), + ( + lazy_fixture(tag_patch_commits.__name__), + default_tag_parser.__name__, + ), + ( + lazy_fixture(tag_minor_commits.__name__), + default_tag_parser.__name__, + ), + ( + lazy_fixture(tag_major_commits.__name__), + default_tag_parser.__name__, + ), + ) ), *( - (TAG_COMMITS_MINOR, True, major_on_zero, "0.3.0-beta.2") + # when prerelease is True, & allow_zero_version is False, the version should be + # 1.0.0, across the board when any valuable change + # is made because of the allow_zero_version is False. major_on_zero is ignored + # when allow_zero_version is False (but we still test it) + (commits, parser, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) + for commits, parser in ( + (None, default_tag_parser.__name__), + ( + lazy_fixture(angular_chore_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(angular_patch_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(angular_minor_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(angular_major_commits.__name__), + default_angular_parser.__name__, + ), + ( + lazy_fixture(emoji_chore_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(emoji_patch_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(emoji_minor_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(emoji_major_commits.__name__), + default_emoji_parser.__name__, + ), + ( + lazy_fixture(scipy_chore_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(scipy_patch_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(scipy_minor_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(scipy_major_commits.__name__), + default_scipy_parser.__name__, + ), + ( + lazy_fixture(tag_chore_commits.__name__), + default_tag_parser.__name__, + ), + ( + lazy_fixture(tag_patch_commits.__name__), + default_tag_parser.__name__, + ), + ( + lazy_fixture(tag_minor_commits.__name__), + default_tag_parser.__name__, + ), + ( + lazy_fixture(tag_major_commits.__name__), + default_tag_parser.__name__, + ), + ) ), - (TAG_COMMITS_MAJOR, False, True, "1.0.0"), - (TAG_COMMITS_MAJOR, True, True, "1.0.0-beta.1"), - (TAG_COMMITS_MAJOR, False, False, "0.3.0"), - # Note - since breaking changes are absorbed into the minor digit - # with major_on_zero = False, and that's already been incremented - # since the last full release, the breaking change here will only - # trigger a prerelease revision - (TAG_COMMITS_MAJOR, True, False, "0.3.0-beta.2"), ], }.items() for ( commit_messages, + parser_fixture_name, prerelease, major_on_zero, + allow_zero_version, expected_new_version, ) in values ], ), ) -def test_algorithm_with_zero_dot_versions_tag( - repo, +def test_algorithm_with_zero_dot_versions_minimums( + repo: Repo, file_in_repo, commit_parser, translator, @@ -1298,15 +1616,21 @@ def test_algorithm_with_zero_dot_versions_tag( prerelease, expected_new_version, major_on_zero, + allow_zero_version, ): - for commit_message in commit_messages: + # Setup + # Move tree down to the Initial Commit + initial_commit = repo.git.log("--max-parents=0", "--format=%H").strip() + repo.git.reset("--hard", initial_commit) + + for commit_message in commit_messages or []: add_text_to_file(repo, file_in_repo) repo.git.commit(m=commit_message) + # Action new_version = next_version( - repo, translator, commit_parser, prerelease, major_on_zero + repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version ) - assert new_version == Version.parse( - expected_new_version, prerelease_token=translator.prerelease_token - ) + # Verify + assert expected_new_version == str(new_version) From efe1672788fd3659f53f60cc2fd72ca1bb9e40c0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 21:42:53 -0500 Subject: [PATCH 114/167] test(next-version): adapt tag commits test for new allow_zero_version config option --- tests/scenario/test_next_version.py | 330 ++++++++++++++++++++++++---- 1 file changed, 290 insertions(+), 40 deletions(-) diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index 9be1237ba..cb01000cd 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -1142,8 +1142,16 @@ def test_algorithm_with_zero_dot_versions_scipy( @pytest.mark.parametrize( - "repo, commit_parser, translator, commit_messages," - "prerelease, major_on_zero, expected_new_version", + str.join(', ', [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ]), xdist_sort_hack( [ ( @@ -1153,105 +1161,347 @@ def test_algorithm_with_zero_dot_versions_scipy( commit_messages, prerelease, major_on_zero, + allow_zero_version, expected_new_version, ) for (repo_fixture_name, parser_fixture_name, translator), values in { # Latest version for repo_with_no_tags is currently 0.0.0 (default) # It's biggest change type is minor, so the next version should be 0.1.0 ( - "repo_with_no_tags_tag_commits", - "default_tag_parser", + repo_with_no_tags_tag_commits.__name__, + default_tag_parser.__name__, VersionTranslator(), ): [ *( - (commits, False, major_on_zero, "0.1.0") + # when prerelease is False, & major_on_zero is False & + # allow_zero_version is True, the version should be + # 0.1.0, with the given commits + (commits, False, False, True, "0.1.0") + for commits in ( + # Even when this test does not change anything, the base modification + # will be a minor change and thus the version will be bumped to 0.1.0 + None, + + # Non version bumping commits are absorbed into the previously detected minor bump + lazy_fixture(tag_chore_commits.__name__), + + # Patch commits are absorbed into the previously detected minor bump + lazy_fixture(tag_patch_commits.__name__), + + # Minor level commits are absorbed into the previously detected minor bump + lazy_fixture(tag_minor_commits.__name__), + + # Given the major_on_zero is False and the version is starting at 0.0.0, + # the major level commits are limited to only causing a minor level bump + lazy_fixture(tag_major_commits.__name__), + ) + ), + + # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, + # the version should only be minor bumped when provided major commits because + # of the major_on_zero value + (lazy_fixture(tag_major_commits.__name__), False, False, True, "0.1.0"), + + # when prerelease is False, & major_on_zero is True & allow_zero_version is True, + # the version should be major bumped when provided major commits because + # of the major_on_zero value + (lazy_fixture(tag_major_commits.__name__), False, True, True, "1.0.0"), + + *( + # when prerelease is False, & allow_zero_version is False, the version should be + # 1.0.0, across the board because 0 is not a valid major version. + # major_on_zero is ignored as it is not relevant but tested for completeness + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) for commits in ( - [], - ["uninteresting"], - TAG_COMMITS_PATCH, - TAG_COMMITS_MINOR, + None, + lazy_fixture(tag_chore_commits.__name__), + lazy_fixture(tag_patch_commits.__name__), + lazy_fixture(tag_minor_commits.__name__), + lazy_fixture(tag_major_commits.__name__), ) ), - (TAG_COMMITS_MAJOR, False, False, "0.1.0"), - (TAG_COMMITS_MAJOR, False, True, "1.0.0"), ], # Latest version for repo_with_single_branch is currently 0.1.1 # Note repo_with_single_branch isn't modelled with prereleases ( - "repo_with_single_branch_tag_commits", - "default_tag_parser", + repo_with_single_branch_tag_commits.__name__, + default_tag_parser.__name__, VersionTranslator(), ): [ *( - (commits, False, major_on_zero, "0.1.1") + # when prerelease must be False, and allow_zero_version is True, + # the version is not bumped because of non valuable changes regardless + # of the major_on_zero value + (commits, False, major_on_zero, True, "0.1.1") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(tag_chore_commits.__name__)) ), *( - (TAG_COMMITS_PATCH, False, major_on_zero, "0.1.2") + # when prerelease must be False, and allow_zero_version is True, + # the version is patch bumped because of the patch level commits + # regardless of the major_on_zero value + (lazy_fixture(tag_patch_commits.__name__), False, major_on_zero, True, "0.1.2") for major_on_zero in (True, False) ), *( - (TAG_COMMITS_MINOR, False, major_on_zero, "0.2.0") + # when prerelease must be False, and allow_zero_version is True, + # the version is minor bumped because of the major_on_zero value=False + (commits, False, False, True, "0.2.0") + for commits in ( + lazy_fixture(tag_minor_commits.__name__), + lazy_fixture(tag_major_commits.__name__), + ) + ), + # when prerelease must be False, and allow_zero_version is True, + # but the major_on_zero is True, then when a major level commit is given, + # the version should be bumped to the next major version + (lazy_fixture(tag_major_commits.__name__), False, True, True, "1.0.0"), + *( + # when prerelease must be False, & allow_zero_version is False, the version should be + # 1.0.0, with any change regardless of major_on_zero + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) + for commits in ( + None, + lazy_fixture(tag_chore_commits.__name__), + lazy_fixture(tag_patch_commits.__name__), + lazy_fixture(tag_minor_commits.__name__), + lazy_fixture(tag_major_commits.__name__), + ) ), - (TAG_COMMITS_MAJOR, False, False, "0.2.0"), - (TAG_COMMITS_MAJOR, False, True, "1.0.0"), ], # Latest version for repo_with_single_branch_and_prereleases is # currently 0.2.0 ( - "repo_with_single_branch_and_prereleases_tag_commits", - "default_tag_parser", + repo_with_single_branch_and_prereleases_tag_commits.__name__, + default_tag_parser.__name__, VersionTranslator(), ): [ *( - (commits, prerelease, major_on_zero, "0.2.0") + # when allow_zero_version is True, the version is not bumped + # regardless of prerelease and major_on_zero values when given + # non valuable changes + (commits, prerelease, major_on_zero, True, "0.2.0") for prerelease in (True, False) for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(tag_chore_commits.__name__)) ), + + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped as a prerelease version, when given patch level commits + (lazy_fixture(tag_patch_commits.__name__), True, False, True, "0.2.1-rc.1"), + + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits + (lazy_fixture(tag_patch_commits.__name__), False, False, True, "0.2.1"), *( - (TAG_COMMITS_PATCH, False, major_on_zero, "0.2.1") - for major_on_zero in (True, False) + # when allow_zero_version is True, + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped as a prerelease version, when given commits of a minor or major level + (commits, True, False, True, "0.3.0-rc.1") + for commits in ( + lazy_fixture(tag_minor_commits.__name__), + lazy_fixture(tag_major_commits.__name__), + ) ), *( - (TAG_COMMITS_PATCH, True, major_on_zero, "0.2.1-rc.1") - for major_on_zero in (True, False) + # when allow_zero_version is True, + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped, when given commits of a minor or major level because + # major_on_zero = False + (commits, False, False, True, "0.3.0") + for commits in ( + lazy_fixture(tag_minor_commits.__name__), + lazy_fixture(tag_major_commits.__name__), + ) ), + + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits + (lazy_fixture(tag_major_commits.__name__), True, True, True, "1.0.0-rc.1"), + + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + (lazy_fixture(tag_major_commits.__name__), False, True, True, "1.0.0"), + *( - (TAG_COMMITS_MINOR, False, major_on_zero, "0.3.0") + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0 as a prerelease version, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-rc.1") for major_on_zero in (True, False) + for commits in ( + None, + lazy_fixture(tag_chore_commits.__name__), + lazy_fixture(tag_patch_commits.__name__), + lazy_fixture(tag_minor_commits.__name__), + lazy_fixture(tag_major_commits.__name__), + ) ), *( - (TAG_COMMITS_MINOR, True, major_on_zero, "0.3.0-rc.1") + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) + for commits in ( + lazy_fixture(tag_patch_commits.__name__), + lazy_fixture(tag_minor_commits.__name__), + lazy_fixture(tag_major_commits.__name__), + ) ), - (TAG_COMMITS_MAJOR, False, True, "1.0.0"), - (TAG_COMMITS_MAJOR, True, True, "1.0.0-rc.1"), - (TAG_COMMITS_MAJOR, False, False, "0.3.0"), - (TAG_COMMITS_MAJOR, True, False, "0.3.0-rc.1"), ], # Latest version for repo_with_main_and_feature_branches is currently # 0.3.0-beta.1. # The last full release version was 0.2.0, so it's had a minor # prerelease ( - "repo_w_github_flow_w_feature_release_channel_tag_commits", - "default_tag_parser", + repo_w_github_flow_w_feature_release_channel_tag_commits.__name__, + default_tag_parser.__name__, VersionTranslator(prerelease_token="beta"), ): [ *( - (commits, True, major_on_zero, "0.3.0-beta.1") + # when prerelease is True, & major_on_zero is True & False, + # the version is not bumped because nothing of importance happened + (commits, True, major_on_zero, True, "0.3.0-beta.1") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(tag_chore_commits.__name__)) ), - # Models a merge of commits from the branch to the main branch, now - # that prerelease=False *( - (commits, False, major_on_zero, "0.3.0") + (commits, True, False, True, "0.3.0-beta.2") + for commits in ( + # when prerelease is True, & major_on_zero is False, the version should be + # increment the next prerelease version, when given patch level commits + # because the last full release was 0.2.0 and the prior prerelease consumes the + # patch bump + lazy_fixture(tag_patch_commits.__name__), + + # when prerelease is True, & major_on_zero is False, the version should be + # minor bumped, when given patch level commits because last full version was 0.2.0 + lazy_fixture(tag_minor_commits.__name__), + + # when prerelease is True, & major_on_zero is False, the version should be + # increment the next prerelease version, when given new breaking changes + # because major_on_zero is false, the last full release was 0.2.0 + # and the prior prerelease consumes the breaking changes + lazy_fixture(tag_major_commits.__name__), + ) + ), + *( + (commits, False, False, True, "0.3.0") + for commits in ( + # Even though we are not making any changes, and prerelease is + # off, we look at the last full version which is 0.2.0 and + # consider the previous minor commit to cause the bump to 0.3.0 + None, + + # same as None, but with a chore commit + lazy_fixture(tag_chore_commits.__name__), + + # when prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits because last full version was 0.2.0 + # and it was previously identified (from the prerelease) that a minor commit + # exists between 0.2.0 and now + lazy_fixture(tag_patch_commits.__name__), + + # when prerelease is False, & major_on_zero is False, the version should be + # minor bumped, when given patch level commits because last full version was 0.2.0 + lazy_fixture(tag_minor_commits.__name__), + + + # when prerelease is False, & major_on_zero is False, the version should be + # minor bumped, when given new breaking changes because + # major_on_zero is false and last full version was 0.2.0 + lazy_fixture(tag_major_commits.__name__), + ) + ), + + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits. The previous prerelease is ignored because of the major + # bump + (lazy_fixture(tag_major_commits.__name__), True, True, True, "1.0.0-beta.1"), + + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + (lazy_fixture(tag_major_commits.__name__), False, True, True, "1.0.0"), + + *( + # Since allow_zero_version is False, the version should be 1.0.0 + # as a prerelease value due to prerelease=True, across the board + # regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-beta.1") for major_on_zero in (True, False) + for commits in ( + # None & chore commits are absorbed into the previously detected minor bump + # and because 0 versions are not allowed + None, + lazy_fixture(tag_chore_commits.__name__), + lazy_fixture(tag_patch_commits.__name__), + lazy_fixture(tag_minor_commits.__name__), + lazy_fixture(tag_major_commits.__name__), + ) + ), + *( + # Since allow_zero_version is False, the version should be 1.0.0 + # across the board regardless of the major_on_zero value + (commits, False, True, False, "1.0.0") + for commits in ( + # None & chore commits are absorbed into the previously detected minor bump + # and because 0 versions are not allowed + None, + + # Same as above, even though our change does not trigger a bump normally + lazy_fixture(tag_chore_commits.__name__), + + # Even though we apply more patch, minor, major commits, the previous + # minor commit (in the prerelase tag) triggers a higher bump & + # with allow_zero_version=False, and ignore prereleases, we bump to 1.0.0 + lazy_fixture(tag_patch_commits.__name__), + lazy_fixture(tag_minor_commits.__name__), + lazy_fixture(tag_major_commits.__name__), + ) + ), + ], + }.items() + for ( + commit_messages, + prerelease, + major_on_zero, + allow_zero_version, + expected_new_version, + ) in values + ], + ), +) +def test_algorithm_with_zero_dot_versions_tag( + repo, + file_in_repo, + commit_parser, + translator, + commit_messages, + prerelease, + expected_new_version, + major_on_zero, + allow_zero_version, +): + # Setup + for commit_message in commit_messages or []: + add_text_to_file(repo, file_in_repo) + repo.git.commit(m=commit_message) + + # Action + new_version = next_version( + repo, translator, commit_parser, prerelease, major_on_zero, allow_zero_version + ) + + # Verify + assert expected_new_version == str(new_version) + @pytest.mark.parametrize( str.join(" ,", [ From 58938315a3ea04db798dc40b7940f9d323a00aae Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 21:57:49 -0500 Subject: [PATCH 115/167] test(next-version): adapt angular commits test for new allow_zero_version config option --- tests/scenario/test_next_version.py | 291 ++++++++++++++++++++++------ 1 file changed, 237 insertions(+), 54 deletions(-) diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index cb01000cd..48807f2cb 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -567,8 +567,16 @@ def test_algorithm_no_zero_dot_versions_tag( @pytest.mark.parametrize( - "repo, commit_parser, translator, commit_messages," - "prerelease, major_on_zero, expected_new_version", + str.join(', ', [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ]), xdist_sort_hack( [ ( @@ -578,6 +586,7 @@ def test_algorithm_no_zero_dot_versions_tag( commit_messages, prerelease, major_on_zero, + allow_zero_version, expected_new_version, ) for (repo_fixture_name, parser_fixture_name, translator), values in { @@ -589,17 +598,54 @@ def test_algorithm_no_zero_dot_versions_tag( VersionTranslator(), ): [ *( - (commits, False, major_on_zero, "0.1.0") + # when prerelease is False, & major_on_zero is False & + # allow_zero_version is True, the version should be + # 0.1.0, with the given commits + (commits, False, False, True, "0.1.0") + for commits in ( + # Even when this test does not change anything, the base modification + # will be a minor change and thus the version will be bumped to 0.1.0 + None, + + # Non version bumping commits are absorbed into the previously detected minor bump + lazy_fixture(angular_chore_commits.__name__), + + # Patch commits are absorbed into the previously detected minor bump + lazy_fixture(angular_patch_commits.__name__), + + # Minor level commits are absorbed into the previously detected minor bump + lazy_fixture(angular_minor_commits.__name__), + + # Given the major_on_zero is False and the version is starting at 0.0.0, + # the major level commits are limited to only causing a minor level bump + lazy_fixture(angular_major_commits.__name__), + ) + ), + + # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, + # the version should only be minor bumped when provided major commits because + # of the major_on_zero value + (lazy_fixture(angular_major_commits.__name__), False, False, True, "0.1.0"), + + # when prerelease is False, & major_on_zero is True & allow_zero_version is True, + # the version should be major bumped when provided major commits because + # of the major_on_zero value + (lazy_fixture(angular_major_commits.__name__), False, True, True, "1.0.0"), + + *( + # when prerelease is False, & allow_zero_version is False, the version should be + # 1.0.0, across the board because 0 is not a valid major version. + # major_on_zero is ignored as it is not relevant but tested for completeness + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) for commits in ( None, lazy_fixture(angular_chore_commits.__name__), lazy_fixture(angular_patch_commits.__name__), lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(angular_major_commits.__name__), ) ), - (lazy_fixture(angular_major_commits.__name__), False, False, "0.1.0"), - (lazy_fixture(angular_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch is currently 0.1.1 # Note repo_with_single_branch isn't modelled with prereleases @@ -609,23 +655,46 @@ def test_algorithm_no_zero_dot_versions_tag( VersionTranslator(), ): [ *( - (commits, False, major_on_zero, "0.1.1") + # when prerelease must be False, and allow_zero_version is True, + # the version is not bumped because of non valuable changes regardless + # of the major_on_zero value + (commits, False, major_on_zero, True, "0.1.1") for major_on_zero in (True, False) - for commits in ( - None, - lazy_fixture(angular_chore_commits.__name__), - ) + for commits in (None, lazy_fixture(angular_chore_commits.__name__)) ), *( - (lazy_fixture(angular_patch_commits.__name__), False, major_on_zero, "0.1.2") + # when prerelease must be False, and allow_zero_version is True, + # the version is patch bumped because of the patch level commits + # regardless of the major_on_zero value + (lazy_fixture(angular_patch_commits.__name__), False, major_on_zero, True, "0.1.2") for major_on_zero in (True, False) ), *( - (lazy_fixture(angular_minor_commits.__name__), False, major_on_zero, "0.2.0") + # when prerelease must be False, and allow_zero_version is True, + # the version is minor bumped because of the major_on_zero value=False + (commits, False, False, True, "0.2.0") + for commits in ( + lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(angular_major_commits.__name__), + ) + ), + # when prerelease must be False, and allow_zero_version is True, + # but the major_on_zero is True, then when a major level commit is given, + # the version should be bumped to the next major version + (lazy_fixture(angular_major_commits.__name__), False, True, True, "1.0.0"), + *( + # when prerelease must be False, & allow_zero_version is False, the version should be + # 1.0.0, with any change regardless of major_on_zero + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(angular_major_commits.__name__), + ) ), - (lazy_fixture(angular_major_commits.__name__), False, False, "0.2.0"), - (lazy_fixture(angular_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch_and_prereleases is # currently 0.2.0 @@ -635,37 +704,84 @@ def test_algorithm_no_zero_dot_versions_tag( VersionTranslator(), ): [ *( - (commits, prerelease, major_on_zero, "0.2.0") + # when allow_zero_version is True, the version is not bumped + # regardless of prerelease and major_on_zero values when given + # non valuable changes + (commits, prerelease, major_on_zero, True, "0.2.0") for prerelease in (True, False) for major_on_zero in (True, False) - for commits in ( - None, - lazy_fixture(angular_chore_commits.__name__) - ) + for commits in (None, lazy_fixture(angular_chore_commits.__name__)) ), + + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped as a prerelease version, when given patch level commits + (lazy_fixture(angular_patch_commits.__name__), True, False, True, "0.2.1-rc.1"), + + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits + (lazy_fixture(angular_patch_commits.__name__), False, False, True, "0.2.1"), *( - (lazy_fixture(angular_patch_commits.__name__), False, major_on_zero, "0.2.1") - for major_on_zero in (True, False) + # when allow_zero_version is True, + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped as a prerelease version, when given commits of a minor or major level + (commits, True, False, True, "0.3.0-rc.1") + for commits in ( + lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(angular_major_commits.__name__), + ) ), *( - (lazy_fixture(angular_patch_commits.__name__), True, major_on_zero, "0.2.1-rc.1") - for major_on_zero in (True, False) + # when allow_zero_version is True, + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped, when given commits of a minor or major level because + # major_on_zero = False + (commits, False, False, True, "0.3.0") + for commits in ( + lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(angular_major_commits.__name__), + ) ), + + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits + (lazy_fixture(angular_major_commits.__name__), True, True, True, "1.0.0-rc.1"), + + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + (lazy_fixture(angular_major_commits.__name__), False, True, True, "1.0.0"), + *( - (lazy_fixture(angular_minor_commits.__name__), False, major_on_zero, "0.3.0") + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0 as a prerelease version, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-rc.1") for major_on_zero in (True, False) + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(angular_major_commits.__name__), + ) ), *( - (lazy_fixture(angular_minor_commits.__name__), True, major_on_zero, "0.3.0-rc.1") + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) + for commits in ( + lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(angular_major_commits.__name__), + ) ), - (lazy_fixture(angular_major_commits.__name__), False, True, "1.0.0"), - (lazy_fixture(angular_major_commits.__name__), True, True, "1.0.0-rc.1"), - (lazy_fixture(angular_major_commits.__name__), False, False, "0.3.0"), - (lazy_fixture(angular_major_commits.__name__), True, False, "0.3.0-rc.1"), ], # Latest version for repo_with_main_and_feature_branches is currently - # 0.3.0-rc.1. + # 0.3.0-beta.1. # The last full release version was 0.2.0, so it's had a minor # prerelease ( @@ -674,50 +790,117 @@ def test_algorithm_no_zero_dot_versions_tag( VersionTranslator(prerelease_token="beta"), ): [ *( - (commits, True, major_on_zero, "0.3.0-beta.1") - for major_on_zero in (True, False) - for commits in (None, lazy_fixture(angular_chore_commits.__name__)) - ), - # Models a merge of commits from the branch to the main branch, now - # that prerelease=False - *( - (commits, False, major_on_zero, "0.3.0") + # when prerelease is True, & major_on_zero is True & False, + # the version is not bumped because nothing of importance happened + (commits, True, major_on_zero, True, "0.3.0-beta.1") for major_on_zero in (True, False) for commits in (None, lazy_fixture(angular_chore_commits.__name__)) ), *( - (lazy_fixture(angular_patch_commits.__name__), False, major_on_zero, "0.3.0") - for major_on_zero in (True, False) + (commits, True, False, True, "0.3.0-beta.2") + for commits in ( + # when prerelease is True, & major_on_zero is False, the version should be + # increment the next prerelease version, when given patch level commits + # because the last full release was 0.2.0 and the prior prerelease consumes the + # patch bump + lazy_fixture(angular_patch_commits.__name__), + + # when prerelease is True, & major_on_zero is False, the version should be + # minor bumped, when given patch level commits because last full version was 0.2.0 + lazy_fixture(angular_minor_commits.__name__), + + # when prerelease is True, & major_on_zero is False, the version should be + # increment the next prerelease version, when given new breaking changes + # because major_on_zero is false, the last full release was 0.2.0 + # and the prior prerelease consumes the breaking changes + lazy_fixture(angular_major_commits.__name__), + ) ), *( - (lazy_fixture(angular_patch_commits.__name__), True, major_on_zero, "0.3.0-beta.2") - for major_on_zero in (True, False) + (commits, False, False, True, "0.3.0") + for commits in ( + # Even though we are not making any changes, and prerelease is + # off, we look at the last full version which is 0.2.0 and + # consider the previous minor commit to cause the bump to 0.3.0 + None, + + # same as None, but with a chore commit + lazy_fixture(angular_chore_commits.__name__), + + # when prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits because last full version was 0.2.0 + # and it was previously identified (from the prerelease) that a minor commit + # exists between 0.2.0 and now + lazy_fixture(angular_patch_commits.__name__), + + # when prerelease is False, & major_on_zero is False, the version should be + # minor bumped, when given patch level commits because last full version was 0.2.0 + lazy_fixture(angular_minor_commits.__name__), + + + # when prerelease is False, & major_on_zero is False, the version should be + # minor bumped, when given new breaking changes because + # major_on_zero is false and last full version was 0.2.0 + lazy_fixture(angular_major_commits.__name__), + ) ), + + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits. The previous prerelease is ignored because of the major + # bump + (lazy_fixture(angular_major_commits.__name__), True, True, True, "1.0.0-beta.1"), + + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + (lazy_fixture(angular_major_commits.__name__), False, True, True, "1.0.0"), + *( - (lazy_fixture(angular_minor_commits.__name__), False, major_on_zero, "0.3.0") + # Since allow_zero_version is False, the version should be 1.0.0 + # as a prerelease value due to prerelease=True, across the board + # regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-beta.1") for major_on_zero in (True, False) + for commits in ( + # None & chore commits are absorbed into the previously detected minor bump + # and because 0 versions are not allowed + None, + lazy_fixture(angular_chore_commits.__name__), + lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(angular_major_commits.__name__), + ) ), *( - (lazy_fixture(angular_minor_commits.__name__), True, major_on_zero, "0.3.0-beta.2") - for major_on_zero in (True, False) + # Since allow_zero_version is False, the version should be 1.0.0 + # across the board regardless of the major_on_zero value + (commits, False, True, False, "1.0.0") + for commits in ( + # None & chore commits are absorbed into the previously detected minor bump + # and because 0 versions are not allowed + None, + + # Same as above, even though our change does not trigger a bump normally + lazy_fixture(angular_chore_commits.__name__), + + # Even though we apply more patch, minor, major commits, the previous + # minor commit (in the prerelase tag) triggers a higher bump & + # with allow_zero_version=False, and ignore prereleases, we bump to 1.0.0 + lazy_fixture(angular_patch_commits.__name__), + lazy_fixture(angular_minor_commits.__name__), + lazy_fixture(angular_major_commits.__name__), + ) ), - (lazy_fixture(angular_major_commits.__name__), False, True, "1.0.0"), - (lazy_fixture(angular_major_commits.__name__), True, True, "1.0.0-beta.1"), - (lazy_fixture(angular_major_commits.__name__), False, False, "0.3.0"), - # Note - since breaking changes are absorbed into the minor digit - # with major_on_zero = False, and that's already been incremented - # since the last full release, the breaking change here will only - # trigger a prerelease revision - (lazy_fixture(angular_major_commits.__name__), True, False, "0.3.0-beta.2"), ], }.items() for ( commit_messages, prerelease, major_on_zero, + allow_zero_version, expected_new_version, ) in values - ] + ], ), ) def test_algorithm_with_zero_dot_versions_angular( From fc05b0e334e5e8f9263626f3b68c32ee3a3b9bcc Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 22:04:19 -0500 Subject: [PATCH 116/167] test(next-version): adapt emoji commits test for new allow_zero_version config option --- tests/scenario/test_next_version.py | 310 ++++++++++++++++++++++------ 1 file changed, 252 insertions(+), 58 deletions(-) diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index 48807f2cb..891a3a9b2 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -47,6 +47,10 @@ scipy_major_commits, scipy_minor_commits, scipy_patch_commits, + repo_w_github_flow_w_feature_release_channel_emoji_commits, + repo_with_no_tags_emoji_commits, + repo_with_single_branch_and_prereleases_emoji_commits, + repo_with_single_branch_emoji_commits, ) from tests.util import add_text_to_file, xdist_sort_hack @@ -929,8 +933,16 @@ def test_algorithm_with_zero_dot_versions_angular( @pytest.mark.parametrize( - "repo, commit_parser, translator, commit_messages," - "prerelease, major_on_zero, expected_new_version", + str.join(', ', [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ]), xdist_sort_hack( [ ( @@ -940,140 +952,321 @@ def test_algorithm_with_zero_dot_versions_angular( commit_messages, prerelease, major_on_zero, + allow_zero_version, expected_new_version, ) for (repo_fixture_name, parser_fixture_name, translator), values in { # Latest version for repo_with_no_tags is currently 0.0.0 (default) # It's biggest change type is minor, so the next version should be 0.1.0 ( - "repo_with_no_tags_emoji_commits", - "default_emoji_parser", + repo_with_no_tags_emoji_commits.__name__, + default_emoji_parser.__name__, VersionTranslator(), ): [ *( - (commits, False, major_on_zero, "0.1.0") + # when prerelease is False, & major_on_zero is False & + # allow_zero_version is True, the version should be + # 0.1.0, with the given commits + (commits, False, False, True, "0.1.0") + for commits in ( + # Even when this test does not change anything, the base modification + # will be a minor change and thus the version will be bumped to 0.1.0 + None, + + # Non version bumping commits are absorbed into the previously detected minor bump + lazy_fixture(emoji_chore_commits.__name__), + + # Patch commits are absorbed into the previously detected minor bump + lazy_fixture(emoji_patch_commits.__name__), + + # Minor level commits are absorbed into the previously detected minor bump + lazy_fixture(emoji_minor_commits.__name__), + + # Given the major_on_zero is False and the version is starting at 0.0.0, + # the major level commits are limited to only causing a minor level bump + lazy_fixture(emoji_major_commits.__name__), + ) + ), + + # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, + # the version should only be minor bumped when provided major commits because + # of the major_on_zero value + (lazy_fixture(emoji_major_commits.__name__), False, False, True, "0.1.0"), + + # when prerelease is False, & major_on_zero is True & allow_zero_version is True, + # the version should be major bumped when provided major commits because + # of the major_on_zero value + (lazy_fixture(emoji_major_commits.__name__), False, True, True, "1.0.0"), + + *( + # when prerelease is False, & allow_zero_version is False, the version should be + # 1.0.0, across the board because 0 is not a valid major version. + # major_on_zero is ignored as it is not relevant but tested for completeness + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) for commits in ( - [], - ["uninteresting"], + None, + lazy_fixture(emoji_chore_commits.__name__), lazy_fixture(emoji_patch_commits.__name__), lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), ) ), - (lazy_fixture(emoji_major_commits.__name__), False, False, "0.1.0"), - (lazy_fixture(emoji_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch is currently 0.1.1 # Note repo_with_single_branch isn't modelled with prereleases ( - "repo_with_single_branch_emoji_commits", - "default_emoji_parser", + repo_with_single_branch_emoji_commits.__name__, + default_emoji_parser.__name__, VersionTranslator(), ): [ *( - (commits, False, major_on_zero, "0.1.1") + # when prerelease must be False, and allow_zero_version is True, + # the version is not bumped because of non valuable changes regardless + # of the major_on_zero value + (commits, False, major_on_zero, True, "0.1.1") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(emoji_chore_commits.__name__)) ), *( - (lazy_fixture(emoji_patch_commits.__name__), False, major_on_zero, "0.1.2") + # when prerelease must be False, and allow_zero_version is True, + # the version is patch bumped because of the patch level commits + # regardless of the major_on_zero value + (lazy_fixture(emoji_patch_commits.__name__), False, major_on_zero, True, "0.1.2") for major_on_zero in (True, False) ), *( - (lazy_fixture(emoji_minor_commits.__name__), False, major_on_zero, "0.2.0") + # when prerelease must be False, and allow_zero_version is True, + # the version is minor bumped because of the major_on_zero value=False + (commits, False, False, True, "0.2.0") + for commits in ( + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) + ), + # when prerelease must be False, and allow_zero_version is True, + # but the major_on_zero is True, then when a major level commit is given, + # the version should be bumped to the next major version + (lazy_fixture(emoji_major_commits.__name__), False, True, True, "1.0.0"), + *( + # when prerelease must be False, & allow_zero_version is False, the version should be + # 1.0.0, with any change regardless of major_on_zero + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) + for commits in ( + None, + lazy_fixture(emoji_chore_commits.__name__), + lazy_fixture(emoji_patch_commits.__name__), + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) ), - (lazy_fixture(emoji_major_commits.__name__), False, False, "0.2.0"), - (lazy_fixture(emoji_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch_and_prereleases is # currently 0.2.0 ( - "repo_with_single_branch_and_prereleases_emoji_commits", - "default_emoji_parser", + repo_with_single_branch_and_prereleases_emoji_commits.__name__, + default_emoji_parser.__name__, VersionTranslator(), ): [ *( - (commits, prerelease, major_on_zero, "0.2.0") + # when allow_zero_version is True, the version is not bumped + # regardless of prerelease and major_on_zero values when given + # non valuable changes + (commits, prerelease, major_on_zero, True, "0.2.0") for prerelease in (True, False) for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(emoji_chore_commits.__name__)) ), + + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped as a prerelease version, when given patch level commits + (lazy_fixture(emoji_patch_commits.__name__), True, False, True, "0.2.1-rc.1"), + + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits + (lazy_fixture(emoji_patch_commits.__name__), False, False, True, "0.2.1"), *( - (lazy_fixture(emoji_patch_commits.__name__), False, major_on_zero, "0.2.1") - for major_on_zero in (True, False) + # when allow_zero_version is True, + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped as a prerelease version, when given commits of a minor or major level + (commits, True, False, True, "0.3.0-rc.1") + for commits in ( + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) ), *( - (lazy_fixture(emoji_patch_commits.__name__), True, major_on_zero, "0.2.1-rc.1") - for major_on_zero in (True, False) + # when allow_zero_version is True, + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped, when given commits of a minor or major level because + # major_on_zero = False + (commits, False, False, True, "0.3.0") + for commits in ( + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) ), + + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits + (lazy_fixture(emoji_major_commits.__name__), True, True, True, "1.0.0-rc.1"), + + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + (lazy_fixture(emoji_major_commits.__name__), False, True, True, "1.0.0"), + *( - (lazy_fixture(emoji_minor_commits.__name__), False, major_on_zero, "0.3.0") + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0 as a prerelease version, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-rc.1") for major_on_zero in (True, False) + for commits in ( + None, + lazy_fixture(emoji_chore_commits.__name__), + lazy_fixture(emoji_patch_commits.__name__), + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) ), *( - (lazy_fixture(emoji_minor_commits.__name__), True, major_on_zero, "0.3.0-rc.1") + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) + for commits in ( + lazy_fixture(emoji_patch_commits.__name__), + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) ), - (lazy_fixture(emoji_major_commits.__name__), False, True, "1.0.0"), - (lazy_fixture(emoji_major_commits.__name__), True, True, "1.0.0-rc.1"), - (lazy_fixture(emoji_major_commits.__name__), False, False, "0.3.0"), - (lazy_fixture(emoji_major_commits.__name__), True, False, "0.3.0-rc.1"), ], # Latest version for repo_with_main_and_feature_branches is currently # 0.3.0-beta.1. # The last full release version was 0.2.0, so it's had a minor # prerelease ( - "repo_w_github_flow_w_feature_release_channel_emoji_commits", - "default_emoji_parser", + repo_w_github_flow_w_feature_release_channel_emoji_commits.__name__, + default_emoji_parser.__name__, VersionTranslator(prerelease_token="beta"), ): [ *( - (commits, True, major_on_zero, "0.3.0-beta.1") - for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) - ), - # Models a merge of commits from the branch to the main branch, now - # that prerelease=False - *( - (commits, False, major_on_zero, "0.3.0") + # when prerelease is True, & major_on_zero is True & False, + # the version is not bumped because nothing of importance happened + (commits, True, major_on_zero, True, "0.3.0-beta.1") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(emoji_chore_commits.__name__)) ), *( - (lazy_fixture(emoji_patch_commits.__name__), False, major_on_zero, "0.3.0") - for major_on_zero in (True, False) + (commits, True, False, True, "0.3.0-beta.2") + for commits in ( + # when prerelease is True, & major_on_zero is False, the version should be + # increment the next prerelease version, when given patch level commits + # because the last full release was 0.2.0 and the prior prerelease consumes the + # patch bump + lazy_fixture(emoji_patch_commits.__name__), + + # when prerelease is True, & major_on_zero is False, the version should be + # minor bumped, when given patch level commits because last full version was 0.2.0 + lazy_fixture(emoji_minor_commits.__name__), + + # when prerelease is True, & major_on_zero is False, the version should be + # increment the next prerelease version, when given new breaking changes + # because major_on_zero is false, the last full release was 0.2.0 + # and the prior prerelease consumes the breaking changes + lazy_fixture(emoji_major_commits.__name__), + ) ), *( - (lazy_fixture(emoji_patch_commits.__name__), True, major_on_zero, "0.3.0-beta.2") - for major_on_zero in (True, False) + (commits, False, False, True, "0.3.0") + for commits in ( + # Even though we are not making any changes, and prerelease is + # off, we look at the last full version which is 0.2.0 and + # consider the previous minor commit to cause the bump to 0.3.0 + None, + + # same as None, but with a chore commit + lazy_fixture(emoji_chore_commits.__name__), + + # when prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits because last full version was 0.2.0 + # and it was previously identified (from the prerelease) that a minor commit + # exists between 0.2.0 and now + lazy_fixture(emoji_patch_commits.__name__), + + # when prerelease is False, & major_on_zero is False, the version should be + # minor bumped, when given patch level commits because last full version was 0.2.0 + lazy_fixture(emoji_minor_commits.__name__), + + + # when prerelease is False, & major_on_zero is False, the version should be + # minor bumped, when given new breaking changes because + # major_on_zero is false and last full version was 0.2.0 + lazy_fixture(emoji_major_commits.__name__), + ) ), + + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits. The previous prerelease is ignored because of the major + # bump + (lazy_fixture(emoji_major_commits.__name__), True, True, True, "1.0.0-beta.1"), + + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + (lazy_fixture(emoji_major_commits.__name__), False, True, True, "1.0.0"), + *( - (lazy_fixture(emoji_minor_commits.__name__), False, major_on_zero, "0.3.0") + # Since allow_zero_version is False, the version should be 1.0.0 + # as a prerelease value due to prerelease=True, across the board + # regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-beta.1") for major_on_zero in (True, False) + for commits in ( + # None & chore commits are absorbed into the previously detected minor bump + # and because 0 versions are not allowed + None, + lazy_fixture(emoji_chore_commits.__name__), + lazy_fixture(emoji_patch_commits.__name__), + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) ), *( - (lazy_fixture(emoji_minor_commits.__name__), True, major_on_zero, "0.3.0-beta.2") - for major_on_zero in (True, False) + # Since allow_zero_version is False, the version should be 1.0.0 + # across the board regardless of the major_on_zero value + (commits, False, True, False, "1.0.0") + for commits in ( + # None & chore commits are absorbed into the previously detected minor bump + # and because 0 versions are not allowed + None, + + # Same as above, even though our change does not trigger a bump normally + lazy_fixture(emoji_chore_commits.__name__), + + # Even though we apply more patch, minor, major commits, the previous + # minor commit (in the prerelase tag) triggers a higher bump & + # with allow_zero_version=False, and ignore prereleases, we bump to 1.0.0 + lazy_fixture(emoji_patch_commits.__name__), + lazy_fixture(emoji_minor_commits.__name__), + lazy_fixture(emoji_major_commits.__name__), + ) ), - (lazy_fixture(emoji_major_commits.__name__), False, True, "1.0.0"), - (lazy_fixture(emoji_major_commits.__name__), True, True, "1.0.0-beta.1"), - (lazy_fixture(emoji_major_commits.__name__), False, False, "0.3.0"), - # Note - since breaking changes are absorbed into the minor digit - # with major_on_zero = False, and that's already been incremented - # since the last full release, the breaking change here will only - # trigger a prerelease revision - (lazy_fixture(emoji_major_commits.__name__), True, False, "0.3.0-beta.2"), ], }.items() for ( commit_messages, prerelease, major_on_zero, + allow_zero_version, expected_new_version, ) in values - ] + ], ), ) def test_algorithm_with_zero_dot_versions_emoji( @@ -1101,6 +1294,7 @@ def test_algorithm_with_zero_dot_versions_emoji( assert expected_new_version == str(new_version) + @pytest.mark.parametrize( "repo, commit_parser, translator, commit_messages," "prerelease, major_on_zero, expected_new_version", From d157ecf579594177de0b7cbc61b706f41db56fb9 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 22:08:15 -0500 Subject: [PATCH 117/167] test(next-version): adapt scipy commits test for new allow_zero_version config option --- tests/scenario/test_next_version.py | 333 ++++++++++++++++++++-------- 1 file changed, 238 insertions(+), 95 deletions(-) diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index 891a3a9b2..66a630641 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -51,6 +51,10 @@ repo_with_no_tags_emoji_commits, repo_with_single_branch_and_prereleases_emoji_commits, repo_with_single_branch_emoji_commits, + repo_w_github_flow_w_feature_release_channel_scipy_commits, + repo_with_no_tags_scipy_commits, + repo_with_single_branch_and_prereleases_scipy_commits, + repo_with_single_branch_scipy_commits, ) from tests.util import add_text_to_file, xdist_sort_hack @@ -1296,8 +1300,16 @@ def test_algorithm_with_zero_dot_versions_emoji( @pytest.mark.parametrize( - "repo, commit_parser, translator, commit_messages," - "prerelease, major_on_zero, expected_new_version", + str.join(', ', [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ]), xdist_sort_hack( [ ( @@ -1307,187 +1319,318 @@ def test_algorithm_with_zero_dot_versions_emoji( commit_messages, prerelease, major_on_zero, + allow_zero_version, expected_new_version, ) for (repo_fixture_name, parser_fixture_name, translator), values in { # Latest version for repo_with_no_tags is currently 0.0.0 (default) # It's biggest change type is minor, so the next version should be 0.1.0 ( - "repo_with_no_tags_scipy_commits", - "default_scipy_parser", + repo_with_no_tags_scipy_commits.__name__, + default_scipy_parser.__name__, VersionTranslator(), ): [ *( - (commits, False, major_on_zero, "0.1.0") + # when prerelease is False, & major_on_zero is False & + # allow_zero_version is True, the version should be + # 0.1.0, with the given commits + (commits, False, False, True, "0.1.0") + for commits in ( + # Even when this test does not change anything, the base modification + # will be a minor change and thus the version will be bumped to 0.1.0 + None, + + # Non version bumping commits are absorbed into the previously detected minor bump + lazy_fixture(scipy_chore_commits.__name__), + + # Patch commits are absorbed into the previously detected minor bump + lazy_fixture(scipy_patch_commits.__name__), + + # Minor level commits are absorbed into the previously detected minor bump + lazy_fixture(scipy_minor_commits.__name__), + + # Given the major_on_zero is False and the version is starting at 0.0.0, + # the major level commits are limited to only causing a minor level bump + lazy_fixture(scipy_major_commits.__name__), + ) + ), + + # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, + # the version should only be minor bumped when provided major commits because + # of the major_on_zero value + (lazy_fixture(scipy_major_commits.__name__), False, False, True, "0.1.0"), + + # when prerelease is False, & major_on_zero is True & allow_zero_version is True, + # the version should be major bumped when provided major commits because + # of the major_on_zero value + (lazy_fixture(scipy_major_commits.__name__), False, True, True, "1.0.0"), + + *( + # when prerelease is False, & allow_zero_version is False, the version should be + # 1.0.0, across the board because 0 is not a valid major version. + # major_on_zero is ignored as it is not relevant but tested for completeness + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) for commits in ( - [], - ["uninteresting"], + None, + lazy_fixture(scipy_chore_commits.__name__), lazy_fixture(scipy_patch_commits.__name__), lazy_fixture(scipy_minor_commits.__name__), + lazy_fixture(scipy_major_commits.__name__), ) ), - (lazy_fixture(scipy_major_commits.__name__), False, False, "0.1.0"), - (lazy_fixture(scipy_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch is currently 0.1.1 # Note repo_with_single_branch isn't modelled with prereleases ( - "repo_with_single_branch_scipy_commits", - "default_scipy_parser", + repo_with_single_branch_scipy_commits.__name__, + default_scipy_parser.__name__, VersionTranslator(), ): [ *( - (commits, False, major_on_zero, "0.1.1") + # when prerelease must be False, and allow_zero_version is True, + # the version is not bumped because of non valuable changes regardless + # of the major_on_zero value + (commits, False, major_on_zero, True, "0.1.1") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(scipy_chore_commits.__name__)) ), *( - ( - lazy_fixture(scipy_patch_commits.__name__), - False, - major_on_zero, - "0.1.2", - ) + # when prerelease must be False, and allow_zero_version is True, + # the version is patch bumped because of the patch level commits + # regardless of the major_on_zero value + (lazy_fixture(scipy_patch_commits.__name__), False, major_on_zero, True, "0.1.2") for major_on_zero in (True, False) ), *( - ( + # when prerelease must be False, and allow_zero_version is True, + # the version is minor bumped because of the major_on_zero value=False + (commits, False, False, True, "0.2.0") + for commits in ( lazy_fixture(scipy_minor_commits.__name__), - False, - major_on_zero, - "0.2.0", + lazy_fixture(scipy_major_commits.__name__), ) + ), + # when prerelease must be False, and allow_zero_version is True, + # but the major_on_zero is True, then when a major level commit is given, + # the version should be bumped to the next major version + (lazy_fixture(scipy_major_commits.__name__), False, True, True, "1.0.0"), + *( + # when prerelease must be False, & allow_zero_version is False, the version should be + # 1.0.0, with any change regardless of major_on_zero + (commits, False, major_on_zero, False, "1.0.0") for major_on_zero in (True, False) + for commits in ( + None, + lazy_fixture(scipy_chore_commits.__name__), + lazy_fixture(scipy_patch_commits.__name__), + lazy_fixture(scipy_minor_commits.__name__), + lazy_fixture(scipy_major_commits.__name__), + ) ), - (lazy_fixture(scipy_major_commits.__name__), False, False, "0.2.0"), - (lazy_fixture(scipy_major_commits.__name__), False, True, "1.0.0"), ], # Latest version for repo_with_single_branch_and_prereleases is # currently 0.2.0 ( - "repo_with_single_branch_and_prereleases_scipy_commits", - "default_scipy_parser", + repo_with_single_branch_and_prereleases_scipy_commits.__name__, + default_scipy_parser.__name__, VersionTranslator(), ): [ *( - (commits, prerelease, major_on_zero, "0.2.0") + # when allow_zero_version is True, the version is not bumped + # regardless of prerelease and major_on_zero values when given + # non valuable changes + (commits, prerelease, major_on_zero, True, "0.2.0") for prerelease in (True, False) for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(scipy_chore_commits.__name__)) ), + + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped as a prerelease version, when given patch level commits + (lazy_fixture(scipy_patch_commits.__name__), True, False, True, "0.2.1-rc.1"), + + # when allow_zero_version is True, + # prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits + (lazy_fixture(scipy_patch_commits.__name__), False, False, True, "0.2.1"), *( - ( - lazy_fixture(scipy_patch_commits.__name__), - False, - major_on_zero, - "0.2.1", + # when allow_zero_version is True, + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped as a prerelease version, when given commits of a minor or major level + (commits, True, False, True, "0.3.0-rc.1") + for commits in ( + lazy_fixture(scipy_minor_commits.__name__), + lazy_fixture(scipy_major_commits.__name__), ) - for major_on_zero in (True, False) ), *( - ( - lazy_fixture(scipy_patch_commits.__name__), - True, - major_on_zero, - "0.2.1-rc.1", + # when allow_zero_version is True, + # prerelease is True, & major_on_zero is False, the version should be + # minor bumped, when given commits of a minor or major level because + # major_on_zero = False + (commits, False, False, True, "0.3.0") + for commits in ( + lazy_fixture(scipy_minor_commits.__name__), + lazy_fixture(scipy_major_commits.__name__), ) - for major_on_zero in (True, False) ), + + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits + (lazy_fixture(scipy_major_commits.__name__), True, True, True, "1.0.0-rc.1"), + + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + (lazy_fixture(scipy_major_commits.__name__), False, True, True, "1.0.0"), + *( - ( + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0 as a prerelease version, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-rc.1") + for major_on_zero in (True, False) + for commits in ( + None, + lazy_fixture(scipy_chore_commits.__name__), + lazy_fixture(scipy_patch_commits.__name__), lazy_fixture(scipy_minor_commits.__name__), - False, - major_on_zero, - "0.3.0", + lazy_fixture(scipy_major_commits.__name__), ) - for major_on_zero in (True, False) ), *( - ( + # when prerelease is True, & allow_zero_version is False, the version should be + # bumped to 1.0.0, when given any/none commits + # because 0.x is no longer a valid version regardless of the major_on_zero value + (commits, False, major_on_zero, False, "1.0.0") + for major_on_zero in (True, False) + for commits in ( + lazy_fixture(scipy_patch_commits.__name__), lazy_fixture(scipy_minor_commits.__name__), - True, - major_on_zero, - "0.3.0-rc.1", + lazy_fixture(scipy_major_commits.__name__), ) - for major_on_zero in (True, False) ), - (lazy_fixture(scipy_major_commits.__name__), False, True, "1.0.0"), - (lazy_fixture(scipy_major_commits.__name__), True, True, "1.0.0-rc.1"), - (lazy_fixture(scipy_major_commits.__name__), False, False, "0.3.0"), - (lazy_fixture(scipy_major_commits.__name__), True, False, "0.3.0-rc.1"), ], # Latest version for repo_with_main_and_feature_branches is currently - # 0.3.0-rc.1. + # 0.3.0-beta.1. # The last full release version was 0.2.0, so it's had a minor # prerelease ( - "repo_w_github_flow_w_feature_release_channel_scipy_commits", - "default_scipy_parser", + repo_w_github_flow_w_feature_release_channel_scipy_commits.__name__, + default_scipy_parser.__name__, VersionTranslator(prerelease_token="beta"), ): [ *( - (commits, True, major_on_zero, "0.3.0-beta.1") - for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) - ), - # Models a merge of commits from the branch to the main branch, now - # that prerelease=False - *( - (commits, False, major_on_zero, "0.3.0") + # when prerelease is True, & major_on_zero is True & False, + # the version is not bumped because nothing of importance happened + (commits, True, major_on_zero, True, "0.3.0-beta.1") for major_on_zero in (True, False) - for commits in ([], ["uninteresting"]) + for commits in (None, lazy_fixture(scipy_chore_commits.__name__)) ), *( - ( + (commits, True, False, True, "0.3.0-beta.2") + for commits in ( + # when prerelease is True, & major_on_zero is False, the version should be + # increment the next prerelease version, when given patch level commits + # because the last full release was 0.2.0 and the prior prerelease consumes the + # patch bump lazy_fixture(scipy_patch_commits.__name__), - False, - major_on_zero, - "0.3.0", + + # when prerelease is True, & major_on_zero is False, the version should be + # minor bumped, when given patch level commits because last full version was 0.2.0 + lazy_fixture(scipy_minor_commits.__name__), + + # when prerelease is True, & major_on_zero is False, the version should be + # increment the next prerelease version, when given new breaking changes + # because major_on_zero is false, the last full release was 0.2.0 + # and the prior prerelease consumes the breaking changes + lazy_fixture(scipy_major_commits.__name__), ) - for major_on_zero in (True, False) ), *( - ( + (commits, False, False, True, "0.3.0") + for commits in ( + # Even though we are not making any changes, and prerelease is + # off, we look at the last full version which is 0.2.0 and + # consider the previous minor commit to cause the bump to 0.3.0 + None, + + # same as None, but with a chore commit + lazy_fixture(scipy_chore_commits.__name__), + + # when prerelease is False, & major_on_zero is False, the version should be + # patch bumped, when given patch level commits because last full version was 0.2.0 + # and it was previously identified (from the prerelease) that a minor commit + # exists between 0.2.0 and now lazy_fixture(scipy_patch_commits.__name__), - True, - major_on_zero, - "0.3.0-beta.2", + + # when prerelease is False, & major_on_zero is False, the version should be + # minor bumped, when given patch level commits because last full version was 0.2.0 + lazy_fixture(scipy_minor_commits.__name__), + + + # when prerelease is False, & major_on_zero is False, the version should be + # minor bumped, when given new breaking changes because + # major_on_zero is false and last full version was 0.2.0 + lazy_fixture(scipy_major_commits.__name__), ) - for major_on_zero in (True, False) ), + + # when prerelease is True, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0 as a prerelease version, when + # given major level commits. The previous prerelease is ignored because of the major + # bump + (lazy_fixture(scipy_major_commits.__name__), True, True, True, "1.0.0-beta.1"), + + # when prerelease is False, & major_on_zero is True, and allow_zero_version + # is True, the version should be bumped to 1.0.0, when given major level commits + (lazy_fixture(scipy_major_commits.__name__), False, True, True, "1.0.0"), + *( - ( + # Since allow_zero_version is False, the version should be 1.0.0 + # as a prerelease value due to prerelease=True, across the board + # regardless of the major_on_zero value + (commits, True, major_on_zero, False, "1.0.0-beta.1") + for major_on_zero in (True, False) + for commits in ( + # None & chore commits are absorbed into the previously detected minor bump + # and because 0 versions are not allowed + None, + lazy_fixture(scipy_chore_commits.__name__), + lazy_fixture(scipy_patch_commits.__name__), lazy_fixture(scipy_minor_commits.__name__), - False, - major_on_zero, - "0.3.0", + lazy_fixture(scipy_major_commits.__name__), ) - for major_on_zero in (True, False) ), *( - ( + # Since allow_zero_version is False, the version should be 1.0.0 + # across the board regardless of the major_on_zero value + (commits, False, True, False, "1.0.0") + for commits in ( + # None & chore commits are absorbed into the previously detected minor bump + # and because 0 versions are not allowed + None, + + # Same as above, even though our change does not trigger a bump normally + lazy_fixture(scipy_chore_commits.__name__), + + # Even though we apply more patch, minor, major commits, the previous + # minor commit (in the prerelase tag) triggers a higher bump & + # with allow_zero_version=False, and ignore prereleases, we bump to 1.0.0 + lazy_fixture(scipy_patch_commits.__name__), lazy_fixture(scipy_minor_commits.__name__), - True, - major_on_zero, - "0.3.0-beta.2", + lazy_fixture(scipy_major_commits.__name__), ) - for major_on_zero in (True, False) ), - (lazy_fixture(scipy_major_commits.__name__), False, True, "1.0.0"), - (lazy_fixture(scipy_major_commits.__name__), True, True, "1.0.0-beta.1"), - (lazy_fixture(scipy_major_commits.__name__), False, False, "0.3.0"), - # Note - since breaking changes are absorbed into the minor digit - # with major_on_zero = False, and that's already been incremented - # since the last full release, the breaking change here will only - # trigger a prerelease revision - (lazy_fixture(scipy_major_commits.__name__), True, False, "0.3.0-beta.2"), ], }.items() for ( commit_messages, prerelease, major_on_zero, + allow_zero_version, expected_new_version, ) in values ], From ced4caadf35b26eb573f6d6e19452d05c7e7dabb Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 22:52:27 -0500 Subject: [PATCH 118/167] style: apply ruff formatting to codebase --- semantic_release/version/algorithm.py | 4 +- tests/fixtures/example_project.py | 2 + tests/fixtures/scipy.py | 4 +- tests/scenario/test_next_version.py | 654 +++++++++++++++++--------- 4 files changed, 436 insertions(+), 228 deletions(-) diff --git a/semantic_release/version/algorithm.py b/semantic_release/version/algorithm.py index f4783a3c5..41c45505e 100644 --- a/semantic_release/version/algorithm.py +++ b/semantic_release/version/algorithm.py @@ -160,9 +160,7 @@ def _increment_version( is in this branch's history. """ local_vars = list(locals().items()) - log.debug( - "_increment_version: %s", ", ".join(f"{k} = {v}" for k, v in local_vars) - ) + log.debug("_increment_version: %s", ", ".join(f"{k} = {v}" for k, v in local_vars)) if latest_version.major == 0: if not allow_zero_version: # Set up default version to be 1.0.0 if currently 0.x.x which means a commented diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 129501909..35d7b7c1b 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -284,6 +284,7 @@ def _update_pyproject_toml(setting: str, value: Any) -> None: @pytest.fixture def set_major_on_zero(update_pyproject_toml: UpdatePyprojectTomlFn) -> SetFlagFn: """Turn on/off the major_on_zero setting.""" + def _set_major_on_zero(flag: bool) -> None: update_pyproject_toml("tool.semantic_release.major_on_zero", flag) @@ -293,6 +294,7 @@ def _set_major_on_zero(flag: bool) -> None: @pytest.fixture def set_allow_zero_version(update_pyproject_toml: UpdatePyprojectTomlFn) -> SetFlagFn: """Turn on/off the allow_zero_version setting.""" + def _set_allow_zero_version(flag: bool) -> None: update_pyproject_toml("tool.semantic_release.allow_zero_version", flag) diff --git a/tests/fixtures/scipy.py b/tests/fixtures/scipy.py index 4efcbecda..0023ece5c 100644 --- a/tests/fixtures/scipy.py +++ b/tests/fixtures/scipy.py @@ -73,9 +73,7 @@ def valid_scipy_commit(scipy_tag, subject, body_parts): @pytest.fixture def scipy_chore_commits(): - return [ - "DOC: Add a note to the documentation" - ] + return ["DOC: Add a note to the documentation"] @pytest.fixture( diff --git a/tests/scenario/test_next_version.py b/tests/scenario/test_next_version.py index 66a630641..ac059b30d 100644 --- a/tests/scenario/test_next_version.py +++ b/tests/scenario/test_next_version.py @@ -9,7 +9,6 @@ from semantic_release.version.algorithm import next_version from semantic_release.version.translator import VersionTranslator -from semantic_release.version.version import Version from tests.const import ( ANGULAR_COMMITS_MAJOR, @@ -23,38 +22,38 @@ TAG_COMMITS_PATCH, ) from tests.fixtures import ( - default_tag_parser, - repo_w_github_flow_w_feature_release_channel_tag_commits, - repo_with_no_tags_tag_commits, - repo_with_single_branch_and_prereleases_tag_commits, - repo_with_single_branch_tag_commits, default_angular_parser, default_emoji_parser, - repo_with_git_flow_angular_commits, - repo_with_git_flow_emoji_commits, - repo_with_git_flow_scipy_commits, - repo_with_git_flow_tag_commits, + default_scipy_parser, + default_tag_parser, + repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_w_github_flow_w_feature_release_channel_emoji_commits, + repo_w_github_flow_w_feature_release_channel_scipy_commits, + repo_w_github_flow_w_feature_release_channel_tag_commits, repo_with_git_flow_and_release_channels_angular_commits, repo_with_git_flow_and_release_channels_emoji_commits, repo_with_git_flow_and_release_channels_scipy_commits, repo_with_git_flow_and_release_channels_tag_commits, - default_scipy_parser, - repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_with_git_flow_angular_commits, + repo_with_git_flow_emoji_commits, + repo_with_git_flow_scipy_commits, + repo_with_git_flow_tag_commits, repo_with_no_tags_angular_commits, + repo_with_no_tags_emoji_commits, + repo_with_no_tags_scipy_commits, + repo_with_no_tags_tag_commits, repo_with_single_branch_and_prereleases_angular_commits, + repo_with_single_branch_and_prereleases_emoji_commits, + repo_with_single_branch_and_prereleases_scipy_commits, + repo_with_single_branch_and_prereleases_tag_commits, repo_with_single_branch_angular_commits, + repo_with_single_branch_emoji_commits, + repo_with_single_branch_scipy_commits, + repo_with_single_branch_tag_commits, scipy_chore_commits, scipy_major_commits, scipy_minor_commits, scipy_patch_commits, - repo_w_github_flow_w_feature_release_channel_emoji_commits, - repo_with_no_tags_emoji_commits, - repo_with_single_branch_and_prereleases_emoji_commits, - repo_with_single_branch_emoji_commits, - repo_w_github_flow_w_feature_release_channel_scipy_commits, - repo_with_no_tags_scipy_commits, - repo_with_single_branch_and_prereleases_scipy_commits, - repo_with_single_branch_scipy_commits, ) from tests.util import add_text_to_file, xdist_sort_hack @@ -66,10 +65,12 @@ def angular_major_commits(): return ANGULAR_COMMITS_MAJOR + @pytest.fixture def angular_minor_commits(): return ANGULAR_COMMITS_MINOR + @pytest.fixture def angular_patch_commits(): return ANGULAR_COMMITS_PATCH @@ -172,11 +173,23 @@ def tag_chore_commits() -> list[str]: ) ), (lazy_fixture(angular_patch_commits.__name__), False, "1.2.0"), - (lazy_fixture(angular_patch_commits.__name__), True, "1.2.0-alpha.3"), + ( + lazy_fixture(angular_patch_commits.__name__), + True, + "1.2.0-alpha.3", + ), (lazy_fixture(angular_minor_commits.__name__), False, "1.2.0"), - (lazy_fixture(angular_minor_commits.__name__), True, "1.2.0-alpha.3"), + ( + lazy_fixture(angular_minor_commits.__name__), + True, + "1.2.0-alpha.3", + ), (lazy_fixture(angular_major_commits.__name__), False, "2.0.0"), - (lazy_fixture(angular_major_commits.__name__), True, "2.0.0-alpha.1"), + ( + lazy_fixture(angular_major_commits.__name__), + True, + "2.0.0-alpha.1", + ), ], # Latest version for repo_with_git_flow_and_release_channels is # currently 1.1.0-alpha.3 @@ -204,11 +217,23 @@ def tag_chore_commits() -> list[str]: ) ), (lazy_fixture(angular_patch_commits.__name__), False, "1.1.0"), - (lazy_fixture(angular_patch_commits.__name__), True, "1.1.0-alpha.4"), + ( + lazy_fixture(angular_patch_commits.__name__), + True, + "1.1.0-alpha.4", + ), (lazy_fixture(angular_minor_commits.__name__), False, "1.1.0"), - (lazy_fixture(angular_minor_commits.__name__), True, "1.1.0-alpha.4"), + ( + lazy_fixture(angular_minor_commits.__name__), + True, + "1.1.0-alpha.4", + ), (lazy_fixture(angular_major_commits.__name__), False, "2.0.0"), - (lazy_fixture(angular_major_commits.__name__), True, "2.0.0-alpha.1"), + ( + lazy_fixture(angular_major_commits.__name__), + True, + "2.0.0-alpha.1", + ), ], }.items() for (commit_messages, prerelease, expected_new_version) in values @@ -575,16 +600,19 @@ def test_algorithm_no_zero_dot_versions_tag( @pytest.mark.parametrize( - str.join(', ', [ - "repo", - "commit_parser", - "translator", - "commit_messages", - "prerelease", - "major_on_zero", - "allow_zero_version", - "expected_new_version", - ]), + str.join( + ", ", + [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ], + ), xdist_sort_hack( [ ( @@ -614,32 +642,37 @@ def test_algorithm_no_zero_dot_versions_tag( # Even when this test does not change anything, the base modification # will be a minor change and thus the version will be bumped to 0.1.0 None, - # Non version bumping commits are absorbed into the previously detected minor bump lazy_fixture(angular_chore_commits.__name__), - # Patch commits are absorbed into the previously detected minor bump lazy_fixture(angular_patch_commits.__name__), - # Minor level commits are absorbed into the previously detected minor bump lazy_fixture(angular_minor_commits.__name__), - # Given the major_on_zero is False and the version is starting at 0.0.0, # the major level commits are limited to only causing a minor level bump lazy_fixture(angular_major_commits.__name__), ) ), - # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, # the version should only be minor bumped when provided major commits because # of the major_on_zero value - (lazy_fixture(angular_major_commits.__name__), False, False, True, "0.1.0"), - + ( + lazy_fixture(angular_major_commits.__name__), + False, + False, + True, + "0.1.0", + ), # when prerelease is False, & major_on_zero is True & allow_zero_version is True, # the version should be major bumped when provided major commits because # of the major_on_zero value - (lazy_fixture(angular_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(angular_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease is False, & allow_zero_version is False, the version should be # 1.0.0, across the board because 0 is not a valid major version. @@ -668,13 +701,22 @@ def test_algorithm_no_zero_dot_versions_tag( # of the major_on_zero value (commits, False, major_on_zero, True, "0.1.1") for major_on_zero in (True, False) - for commits in (None, lazy_fixture(angular_chore_commits.__name__)) + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + ) ), *( # when prerelease must be False, and allow_zero_version is True, # the version is patch bumped because of the patch level commits # regardless of the major_on_zero value - (lazy_fixture(angular_patch_commits.__name__), False, major_on_zero, True, "0.1.2") + ( + lazy_fixture(angular_patch_commits.__name__), + False, + major_on_zero, + True, + "0.1.2", + ) for major_on_zero in (True, False) ), *( @@ -689,7 +731,13 @@ def test_algorithm_no_zero_dot_versions_tag( # when prerelease must be False, and allow_zero_version is True, # but the major_on_zero is True, then when a major level commit is given, # the version should be bumped to the next major version - (lazy_fixture(angular_major_commits.__name__), False, True, True, "1.0.0"), + ( + lazy_fixture(angular_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease must be False, & allow_zero_version is False, the version should be # 1.0.0, with any change regardless of major_on_zero @@ -718,18 +766,31 @@ def test_algorithm_no_zero_dot_versions_tag( (commits, prerelease, major_on_zero, True, "0.2.0") for prerelease in (True, False) for major_on_zero in (True, False) - for commits in (None, lazy_fixture(angular_chore_commits.__name__)) + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + ) ), - # when allow_zero_version is True, # prerelease is False, & major_on_zero is False, the version should be # patch bumped as a prerelease version, when given patch level commits - (lazy_fixture(angular_patch_commits.__name__), True, False, True, "0.2.1-rc.1"), - + ( + lazy_fixture(angular_patch_commits.__name__), + True, + False, + True, + "0.2.1-rc.1", + ), # when allow_zero_version is True, # prerelease is False, & major_on_zero is False, the version should be # patch bumped, when given patch level commits - (lazy_fixture(angular_patch_commits.__name__), False, False, True, "0.2.1"), + ( + lazy_fixture(angular_patch_commits.__name__), + False, + False, + True, + "0.2.1", + ), *( # when allow_zero_version is True, # prerelease is True, & major_on_zero is False, the version should be @@ -751,16 +812,25 @@ def test_algorithm_no_zero_dot_versions_tag( lazy_fixture(angular_major_commits.__name__), ) ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0 as a prerelease version, when # given major level commits - (lazy_fixture(angular_major_commits.__name__), True, True, True, "1.0.0-rc.1"), - + ( + lazy_fixture(angular_major_commits.__name__), + True, + True, + True, + "1.0.0-rc.1", + ), # when prerelease is False, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0, when given major level commits - (lazy_fixture(angular_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(angular_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease is True, & allow_zero_version is False, the version should be # bumped to 1.0.0 as a prerelease version, when given any/none commits @@ -802,7 +872,10 @@ def test_algorithm_no_zero_dot_versions_tag( # the version is not bumped because nothing of importance happened (commits, True, major_on_zero, True, "0.3.0-beta.1") for major_on_zero in (True, False) - for commits in (None, lazy_fixture(angular_chore_commits.__name__)) + for commits in ( + None, + lazy_fixture(angular_chore_commits.__name__), + ) ), *( (commits, True, False, True, "0.3.0-beta.2") @@ -812,11 +885,9 @@ def test_algorithm_no_zero_dot_versions_tag( # because the last full release was 0.2.0 and the prior prerelease consumes the # patch bump lazy_fixture(angular_patch_commits.__name__), - # when prerelease is True, & major_on_zero is False, the version should be # minor bumped, when given patch level commits because last full version was 0.2.0 lazy_fixture(angular_minor_commits.__name__), - # when prerelease is True, & major_on_zero is False, the version should be # increment the next prerelease version, when given new breaking changes # because major_on_zero is false, the last full release was 0.2.0 @@ -831,38 +902,42 @@ def test_algorithm_no_zero_dot_versions_tag( # off, we look at the last full version which is 0.2.0 and # consider the previous minor commit to cause the bump to 0.3.0 None, - # same as None, but with a chore commit lazy_fixture(angular_chore_commits.__name__), - # when prerelease is False, & major_on_zero is False, the version should be # patch bumped, when given patch level commits because last full version was 0.2.0 # and it was previously identified (from the prerelease) that a minor commit # exists between 0.2.0 and now lazy_fixture(angular_patch_commits.__name__), - # when prerelease is False, & major_on_zero is False, the version should be # minor bumped, when given patch level commits because last full version was 0.2.0 lazy_fixture(angular_minor_commits.__name__), - - # when prerelease is False, & major_on_zero is False, the version should be # minor bumped, when given new breaking changes because # major_on_zero is false and last full version was 0.2.0 lazy_fixture(angular_major_commits.__name__), ) ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0 as a prerelease version, when # given major level commits. The previous prerelease is ignored because of the major # bump - (lazy_fixture(angular_major_commits.__name__), True, True, True, "1.0.0-beta.1"), - + ( + lazy_fixture(angular_major_commits.__name__), + True, + True, + True, + "1.0.0-beta.1", + ), # when prerelease is False, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0, when given major level commits - (lazy_fixture(angular_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(angular_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # Since allow_zero_version is False, the version should be 1.0.0 # as a prerelease value due to prerelease=True, across the board @@ -887,10 +962,8 @@ def test_algorithm_no_zero_dot_versions_tag( # None & chore commits are absorbed into the previously detected minor bump # and because 0 versions are not allowed None, - # Same as above, even though our change does not trigger a bump normally lazy_fixture(angular_chore_commits.__name__), - # Even though we apply more patch, minor, major commits, the previous # minor commit (in the prerelase tag) triggers a higher bump & # with allow_zero_version=False, and ignore prereleases, we bump to 1.0.0 @@ -937,16 +1010,19 @@ def test_algorithm_with_zero_dot_versions_angular( @pytest.mark.parametrize( - str.join(', ', [ - "repo", - "commit_parser", - "translator", - "commit_messages", - "prerelease", - "major_on_zero", - "allow_zero_version", - "expected_new_version", - ]), + str.join( + ", ", + [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ], + ), xdist_sort_hack( [ ( @@ -976,32 +1052,37 @@ def test_algorithm_with_zero_dot_versions_angular( # Even when this test does not change anything, the base modification # will be a minor change and thus the version will be bumped to 0.1.0 None, - # Non version bumping commits are absorbed into the previously detected minor bump lazy_fixture(emoji_chore_commits.__name__), - # Patch commits are absorbed into the previously detected minor bump lazy_fixture(emoji_patch_commits.__name__), - # Minor level commits are absorbed into the previously detected minor bump lazy_fixture(emoji_minor_commits.__name__), - # Given the major_on_zero is False and the version is starting at 0.0.0, # the major level commits are limited to only causing a minor level bump lazy_fixture(emoji_major_commits.__name__), ) ), - # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, # the version should only be minor bumped when provided major commits because # of the major_on_zero value - (lazy_fixture(emoji_major_commits.__name__), False, False, True, "0.1.0"), - + ( + lazy_fixture(emoji_major_commits.__name__), + False, + False, + True, + "0.1.0", + ), # when prerelease is False, & major_on_zero is True & allow_zero_version is True, # the version should be major bumped when provided major commits because # of the major_on_zero value - (lazy_fixture(emoji_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(emoji_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease is False, & allow_zero_version is False, the version should be # 1.0.0, across the board because 0 is not a valid major version. @@ -1030,13 +1111,22 @@ def test_algorithm_with_zero_dot_versions_angular( # of the major_on_zero value (commits, False, major_on_zero, True, "0.1.1") for major_on_zero in (True, False) - for commits in (None, lazy_fixture(emoji_chore_commits.__name__)) + for commits in ( + None, + lazy_fixture(emoji_chore_commits.__name__), + ) ), *( # when prerelease must be False, and allow_zero_version is True, # the version is patch bumped because of the patch level commits # regardless of the major_on_zero value - (lazy_fixture(emoji_patch_commits.__name__), False, major_on_zero, True, "0.1.2") + ( + lazy_fixture(emoji_patch_commits.__name__), + False, + major_on_zero, + True, + "0.1.2", + ) for major_on_zero in (True, False) ), *( @@ -1051,7 +1141,13 @@ def test_algorithm_with_zero_dot_versions_angular( # when prerelease must be False, and allow_zero_version is True, # but the major_on_zero is True, then when a major level commit is given, # the version should be bumped to the next major version - (lazy_fixture(emoji_major_commits.__name__), False, True, True, "1.0.0"), + ( + lazy_fixture(emoji_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease must be False, & allow_zero_version is False, the version should be # 1.0.0, with any change regardless of major_on_zero @@ -1080,18 +1176,31 @@ def test_algorithm_with_zero_dot_versions_angular( (commits, prerelease, major_on_zero, True, "0.2.0") for prerelease in (True, False) for major_on_zero in (True, False) - for commits in (None, lazy_fixture(emoji_chore_commits.__name__)) + for commits in ( + None, + lazy_fixture(emoji_chore_commits.__name__), + ) ), - # when allow_zero_version is True, # prerelease is False, & major_on_zero is False, the version should be # patch bumped as a prerelease version, when given patch level commits - (lazy_fixture(emoji_patch_commits.__name__), True, False, True, "0.2.1-rc.1"), - + ( + lazy_fixture(emoji_patch_commits.__name__), + True, + False, + True, + "0.2.1-rc.1", + ), # when allow_zero_version is True, # prerelease is False, & major_on_zero is False, the version should be # patch bumped, when given patch level commits - (lazy_fixture(emoji_patch_commits.__name__), False, False, True, "0.2.1"), + ( + lazy_fixture(emoji_patch_commits.__name__), + False, + False, + True, + "0.2.1", + ), *( # when allow_zero_version is True, # prerelease is True, & major_on_zero is False, the version should be @@ -1113,16 +1222,25 @@ def test_algorithm_with_zero_dot_versions_angular( lazy_fixture(emoji_major_commits.__name__), ) ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0 as a prerelease version, when # given major level commits - (lazy_fixture(emoji_major_commits.__name__), True, True, True, "1.0.0-rc.1"), - + ( + lazy_fixture(emoji_major_commits.__name__), + True, + True, + True, + "1.0.0-rc.1", + ), # when prerelease is False, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0, when given major level commits - (lazy_fixture(emoji_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(emoji_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease is True, & allow_zero_version is False, the version should be # bumped to 1.0.0 as a prerelease version, when given any/none commits @@ -1164,7 +1282,10 @@ def test_algorithm_with_zero_dot_versions_angular( # the version is not bumped because nothing of importance happened (commits, True, major_on_zero, True, "0.3.0-beta.1") for major_on_zero in (True, False) - for commits in (None, lazy_fixture(emoji_chore_commits.__name__)) + for commits in ( + None, + lazy_fixture(emoji_chore_commits.__name__), + ) ), *( (commits, True, False, True, "0.3.0-beta.2") @@ -1174,11 +1295,9 @@ def test_algorithm_with_zero_dot_versions_angular( # because the last full release was 0.2.0 and the prior prerelease consumes the # patch bump lazy_fixture(emoji_patch_commits.__name__), - # when prerelease is True, & major_on_zero is False, the version should be # minor bumped, when given patch level commits because last full version was 0.2.0 lazy_fixture(emoji_minor_commits.__name__), - # when prerelease is True, & major_on_zero is False, the version should be # increment the next prerelease version, when given new breaking changes # because major_on_zero is false, the last full release was 0.2.0 @@ -1193,38 +1312,42 @@ def test_algorithm_with_zero_dot_versions_angular( # off, we look at the last full version which is 0.2.0 and # consider the previous minor commit to cause the bump to 0.3.0 None, - # same as None, but with a chore commit lazy_fixture(emoji_chore_commits.__name__), - # when prerelease is False, & major_on_zero is False, the version should be # patch bumped, when given patch level commits because last full version was 0.2.0 # and it was previously identified (from the prerelease) that a minor commit # exists between 0.2.0 and now lazy_fixture(emoji_patch_commits.__name__), - # when prerelease is False, & major_on_zero is False, the version should be # minor bumped, when given patch level commits because last full version was 0.2.0 lazy_fixture(emoji_minor_commits.__name__), - - # when prerelease is False, & major_on_zero is False, the version should be # minor bumped, when given new breaking changes because # major_on_zero is false and last full version was 0.2.0 lazy_fixture(emoji_major_commits.__name__), ) ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0 as a prerelease version, when # given major level commits. The previous prerelease is ignored because of the major # bump - (lazy_fixture(emoji_major_commits.__name__), True, True, True, "1.0.0-beta.1"), - + ( + lazy_fixture(emoji_major_commits.__name__), + True, + True, + True, + "1.0.0-beta.1", + ), # when prerelease is False, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0, when given major level commits - (lazy_fixture(emoji_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(emoji_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # Since allow_zero_version is False, the version should be 1.0.0 # as a prerelease value due to prerelease=True, across the board @@ -1249,10 +1372,8 @@ def test_algorithm_with_zero_dot_versions_angular( # None & chore commits are absorbed into the previously detected minor bump # and because 0 versions are not allowed None, - # Same as above, even though our change does not trigger a bump normally lazy_fixture(emoji_chore_commits.__name__), - # Even though we apply more patch, minor, major commits, the previous # minor commit (in the prerelase tag) triggers a higher bump & # with allow_zero_version=False, and ignore prereleases, we bump to 1.0.0 @@ -1298,18 +1419,20 @@ def test_algorithm_with_zero_dot_versions_emoji( assert expected_new_version == str(new_version) - @pytest.mark.parametrize( - str.join(', ', [ - "repo", - "commit_parser", - "translator", - "commit_messages", - "prerelease", - "major_on_zero", - "allow_zero_version", - "expected_new_version", - ]), + str.join( + ", ", + [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ], + ), xdist_sort_hack( [ ( @@ -1339,32 +1462,37 @@ def test_algorithm_with_zero_dot_versions_emoji( # Even when this test does not change anything, the base modification # will be a minor change and thus the version will be bumped to 0.1.0 None, - # Non version bumping commits are absorbed into the previously detected minor bump lazy_fixture(scipy_chore_commits.__name__), - # Patch commits are absorbed into the previously detected minor bump lazy_fixture(scipy_patch_commits.__name__), - # Minor level commits are absorbed into the previously detected minor bump lazy_fixture(scipy_minor_commits.__name__), - # Given the major_on_zero is False and the version is starting at 0.0.0, # the major level commits are limited to only causing a minor level bump lazy_fixture(scipy_major_commits.__name__), ) ), - # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, # the version should only be minor bumped when provided major commits because # of the major_on_zero value - (lazy_fixture(scipy_major_commits.__name__), False, False, True, "0.1.0"), - + ( + lazy_fixture(scipy_major_commits.__name__), + False, + False, + True, + "0.1.0", + ), # when prerelease is False, & major_on_zero is True & allow_zero_version is True, # the version should be major bumped when provided major commits because # of the major_on_zero value - (lazy_fixture(scipy_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(scipy_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease is False, & allow_zero_version is False, the version should be # 1.0.0, across the board because 0 is not a valid major version. @@ -1393,13 +1521,22 @@ def test_algorithm_with_zero_dot_versions_emoji( # of the major_on_zero value (commits, False, major_on_zero, True, "0.1.1") for major_on_zero in (True, False) - for commits in (None, lazy_fixture(scipy_chore_commits.__name__)) + for commits in ( + None, + lazy_fixture(scipy_chore_commits.__name__), + ) ), *( # when prerelease must be False, and allow_zero_version is True, # the version is patch bumped because of the patch level commits # regardless of the major_on_zero value - (lazy_fixture(scipy_patch_commits.__name__), False, major_on_zero, True, "0.1.2") + ( + lazy_fixture(scipy_patch_commits.__name__), + False, + major_on_zero, + True, + "0.1.2", + ) for major_on_zero in (True, False) ), *( @@ -1414,7 +1551,13 @@ def test_algorithm_with_zero_dot_versions_emoji( # when prerelease must be False, and allow_zero_version is True, # but the major_on_zero is True, then when a major level commit is given, # the version should be bumped to the next major version - (lazy_fixture(scipy_major_commits.__name__), False, True, True, "1.0.0"), + ( + lazy_fixture(scipy_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease must be False, & allow_zero_version is False, the version should be # 1.0.0, with any change regardless of major_on_zero @@ -1443,18 +1586,31 @@ def test_algorithm_with_zero_dot_versions_emoji( (commits, prerelease, major_on_zero, True, "0.2.0") for prerelease in (True, False) for major_on_zero in (True, False) - for commits in (None, lazy_fixture(scipy_chore_commits.__name__)) + for commits in ( + None, + lazy_fixture(scipy_chore_commits.__name__), + ) ), - # when allow_zero_version is True, # prerelease is False, & major_on_zero is False, the version should be # patch bumped as a prerelease version, when given patch level commits - (lazy_fixture(scipy_patch_commits.__name__), True, False, True, "0.2.1-rc.1"), - + ( + lazy_fixture(scipy_patch_commits.__name__), + True, + False, + True, + "0.2.1-rc.1", + ), # when allow_zero_version is True, # prerelease is False, & major_on_zero is False, the version should be # patch bumped, when given patch level commits - (lazy_fixture(scipy_patch_commits.__name__), False, False, True, "0.2.1"), + ( + lazy_fixture(scipy_patch_commits.__name__), + False, + False, + True, + "0.2.1", + ), *( # when allow_zero_version is True, # prerelease is True, & major_on_zero is False, the version should be @@ -1476,16 +1632,25 @@ def test_algorithm_with_zero_dot_versions_emoji( lazy_fixture(scipy_major_commits.__name__), ) ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0 as a prerelease version, when # given major level commits - (lazy_fixture(scipy_major_commits.__name__), True, True, True, "1.0.0-rc.1"), - + ( + lazy_fixture(scipy_major_commits.__name__), + True, + True, + True, + "1.0.0-rc.1", + ), # when prerelease is False, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0, when given major level commits - (lazy_fixture(scipy_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(scipy_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease is True, & allow_zero_version is False, the version should be # bumped to 1.0.0 as a prerelease version, when given any/none commits @@ -1527,7 +1692,10 @@ def test_algorithm_with_zero_dot_versions_emoji( # the version is not bumped because nothing of importance happened (commits, True, major_on_zero, True, "0.3.0-beta.1") for major_on_zero in (True, False) - for commits in (None, lazy_fixture(scipy_chore_commits.__name__)) + for commits in ( + None, + lazy_fixture(scipy_chore_commits.__name__), + ) ), *( (commits, True, False, True, "0.3.0-beta.2") @@ -1537,11 +1705,9 @@ def test_algorithm_with_zero_dot_versions_emoji( # because the last full release was 0.2.0 and the prior prerelease consumes the # patch bump lazy_fixture(scipy_patch_commits.__name__), - # when prerelease is True, & major_on_zero is False, the version should be # minor bumped, when given patch level commits because last full version was 0.2.0 lazy_fixture(scipy_minor_commits.__name__), - # when prerelease is True, & major_on_zero is False, the version should be # increment the next prerelease version, when given new breaking changes # because major_on_zero is false, the last full release was 0.2.0 @@ -1556,38 +1722,42 @@ def test_algorithm_with_zero_dot_versions_emoji( # off, we look at the last full version which is 0.2.0 and # consider the previous minor commit to cause the bump to 0.3.0 None, - # same as None, but with a chore commit lazy_fixture(scipy_chore_commits.__name__), - # when prerelease is False, & major_on_zero is False, the version should be # patch bumped, when given patch level commits because last full version was 0.2.0 # and it was previously identified (from the prerelease) that a minor commit # exists between 0.2.0 and now lazy_fixture(scipy_patch_commits.__name__), - # when prerelease is False, & major_on_zero is False, the version should be # minor bumped, when given patch level commits because last full version was 0.2.0 lazy_fixture(scipy_minor_commits.__name__), - - # when prerelease is False, & major_on_zero is False, the version should be # minor bumped, when given new breaking changes because # major_on_zero is false and last full version was 0.2.0 lazy_fixture(scipy_major_commits.__name__), ) ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0 as a prerelease version, when # given major level commits. The previous prerelease is ignored because of the major # bump - (lazy_fixture(scipy_major_commits.__name__), True, True, True, "1.0.0-beta.1"), - + ( + lazy_fixture(scipy_major_commits.__name__), + True, + True, + True, + "1.0.0-beta.1", + ), # when prerelease is False, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0, when given major level commits - (lazy_fixture(scipy_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(scipy_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # Since allow_zero_version is False, the version should be 1.0.0 # as a prerelease value due to prerelease=True, across the board @@ -1612,10 +1782,8 @@ def test_algorithm_with_zero_dot_versions_emoji( # None & chore commits are absorbed into the previously detected minor bump # and because 0 versions are not allowed None, - # Same as above, even though our change does not trigger a bump normally lazy_fixture(scipy_chore_commits.__name__), - # Even though we apply more patch, minor, major commits, the previous # minor commit (in the prerelase tag) triggers a higher bump & # with allow_zero_version=False, and ignore prereleases, we bump to 1.0.0 @@ -1662,16 +1830,19 @@ def test_algorithm_with_zero_dot_versions_scipy( @pytest.mark.parametrize( - str.join(', ', [ - "repo", - "commit_parser", - "translator", - "commit_messages", - "prerelease", - "major_on_zero", - "allow_zero_version", - "expected_new_version", - ]), + str.join( + ", ", + [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ], + ), xdist_sort_hack( [ ( @@ -1701,32 +1872,37 @@ def test_algorithm_with_zero_dot_versions_scipy( # Even when this test does not change anything, the base modification # will be a minor change and thus the version will be bumped to 0.1.0 None, - # Non version bumping commits are absorbed into the previously detected minor bump lazy_fixture(tag_chore_commits.__name__), - # Patch commits are absorbed into the previously detected minor bump lazy_fixture(tag_patch_commits.__name__), - # Minor level commits are absorbed into the previously detected minor bump lazy_fixture(tag_minor_commits.__name__), - # Given the major_on_zero is False and the version is starting at 0.0.0, # the major level commits are limited to only causing a minor level bump lazy_fixture(tag_major_commits.__name__), ) ), - # when prerelease is False, & major_on_zero is False, & allow_zero_version is True, # the version should only be minor bumped when provided major commits because # of the major_on_zero value - (lazy_fixture(tag_major_commits.__name__), False, False, True, "0.1.0"), - + ( + lazy_fixture(tag_major_commits.__name__), + False, + False, + True, + "0.1.0", + ), # when prerelease is False, & major_on_zero is True & allow_zero_version is True, # the version should be major bumped when provided major commits because # of the major_on_zero value - (lazy_fixture(tag_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(tag_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease is False, & allow_zero_version is False, the version should be # 1.0.0, across the board because 0 is not a valid major version. @@ -1761,7 +1937,13 @@ def test_algorithm_with_zero_dot_versions_scipy( # when prerelease must be False, and allow_zero_version is True, # the version is patch bumped because of the patch level commits # regardless of the major_on_zero value - (lazy_fixture(tag_patch_commits.__name__), False, major_on_zero, True, "0.1.2") + ( + lazy_fixture(tag_patch_commits.__name__), + False, + major_on_zero, + True, + "0.1.2", + ) for major_on_zero in (True, False) ), *( @@ -1776,7 +1958,13 @@ def test_algorithm_with_zero_dot_versions_scipy( # when prerelease must be False, and allow_zero_version is True, # but the major_on_zero is True, then when a major level commit is given, # the version should be bumped to the next major version - (lazy_fixture(tag_major_commits.__name__), False, True, True, "1.0.0"), + ( + lazy_fixture(tag_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease must be False, & allow_zero_version is False, the version should be # 1.0.0, with any change regardless of major_on_zero @@ -1807,16 +1995,26 @@ def test_algorithm_with_zero_dot_versions_scipy( for major_on_zero in (True, False) for commits in (None, lazy_fixture(tag_chore_commits.__name__)) ), - # when allow_zero_version is True, # prerelease is False, & major_on_zero is False, the version should be # patch bumped as a prerelease version, when given patch level commits - (lazy_fixture(tag_patch_commits.__name__), True, False, True, "0.2.1-rc.1"), - + ( + lazy_fixture(tag_patch_commits.__name__), + True, + False, + True, + "0.2.1-rc.1", + ), # when allow_zero_version is True, # prerelease is False, & major_on_zero is False, the version should be # patch bumped, when given patch level commits - (lazy_fixture(tag_patch_commits.__name__), False, False, True, "0.2.1"), + ( + lazy_fixture(tag_patch_commits.__name__), + False, + False, + True, + "0.2.1", + ), *( # when allow_zero_version is True, # prerelease is True, & major_on_zero is False, the version should be @@ -1838,16 +2036,25 @@ def test_algorithm_with_zero_dot_versions_scipy( lazy_fixture(tag_major_commits.__name__), ) ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0 as a prerelease version, when # given major level commits - (lazy_fixture(tag_major_commits.__name__), True, True, True, "1.0.0-rc.1"), - + ( + lazy_fixture(tag_major_commits.__name__), + True, + True, + True, + "1.0.0-rc.1", + ), # when prerelease is False, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0, when given major level commits - (lazy_fixture(tag_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(tag_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # when prerelease is True, & allow_zero_version is False, the version should be # bumped to 1.0.0 as a prerelease version, when given any/none commits @@ -1899,11 +2106,9 @@ def test_algorithm_with_zero_dot_versions_scipy( # because the last full release was 0.2.0 and the prior prerelease consumes the # patch bump lazy_fixture(tag_patch_commits.__name__), - # when prerelease is True, & major_on_zero is False, the version should be # minor bumped, when given patch level commits because last full version was 0.2.0 lazy_fixture(tag_minor_commits.__name__), - # when prerelease is True, & major_on_zero is False, the version should be # increment the next prerelease version, when given new breaking changes # because major_on_zero is false, the last full release was 0.2.0 @@ -1918,38 +2123,42 @@ def test_algorithm_with_zero_dot_versions_scipy( # off, we look at the last full version which is 0.2.0 and # consider the previous minor commit to cause the bump to 0.3.0 None, - # same as None, but with a chore commit lazy_fixture(tag_chore_commits.__name__), - # when prerelease is False, & major_on_zero is False, the version should be # patch bumped, when given patch level commits because last full version was 0.2.0 # and it was previously identified (from the prerelease) that a minor commit # exists between 0.2.0 and now lazy_fixture(tag_patch_commits.__name__), - # when prerelease is False, & major_on_zero is False, the version should be # minor bumped, when given patch level commits because last full version was 0.2.0 lazy_fixture(tag_minor_commits.__name__), - - # when prerelease is False, & major_on_zero is False, the version should be # minor bumped, when given new breaking changes because # major_on_zero is false and last full version was 0.2.0 lazy_fixture(tag_major_commits.__name__), ) ), - # when prerelease is True, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0 as a prerelease version, when # given major level commits. The previous prerelease is ignored because of the major # bump - (lazy_fixture(tag_major_commits.__name__), True, True, True, "1.0.0-beta.1"), - + ( + lazy_fixture(tag_major_commits.__name__), + True, + True, + True, + "1.0.0-beta.1", + ), # when prerelease is False, & major_on_zero is True, and allow_zero_version # is True, the version should be bumped to 1.0.0, when given major level commits - (lazy_fixture(tag_major_commits.__name__), False, True, True, "1.0.0"), - + ( + lazy_fixture(tag_major_commits.__name__), + False, + True, + True, + "1.0.0", + ), *( # Since allow_zero_version is False, the version should be 1.0.0 # as a prerelease value due to prerelease=True, across the board @@ -1974,10 +2183,8 @@ def test_algorithm_with_zero_dot_versions_scipy( # None & chore commits are absorbed into the previously detected minor bump # and because 0 versions are not allowed None, - # Same as above, even though our change does not trigger a bump normally lazy_fixture(tag_chore_commits.__name__), - # Even though we apply more patch, minor, major commits, the previous # minor commit (in the prerelase tag) triggers a higher bump & # with allow_zero_version=False, and ignore prereleases, we bump to 1.0.0 @@ -2024,16 +2231,19 @@ def test_algorithm_with_zero_dot_versions_tag( @pytest.mark.parametrize( - str.join(" ,", [ - "repo", - "commit_parser", - "translator", - "commit_messages", - "prerelease", - "major_on_zero", - "allow_zero_version", - "expected_new_version", - ]), + str.join( + " ,", + [ + "repo", + "commit_parser", + "translator", + "commit_messages", + "prerelease", + "major_on_zero", + "allow_zero_version", + "expected_new_version", + ], + ), xdist_sort_hack( [ ( From d69a69bb636c06d384ae29601c62971cf1d6e88a Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 3 Mar 2024 22:13:59 -0500 Subject: [PATCH 119/167] chore(stalebot): add config to manage aging issues & PRs --- .github/stale.yml | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..68c4b43eb --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,58 @@ +# Configuration for probot-stale # +# CAO: https://github.com/probot/stale/tree/4232daafc73492864b559b55c9cf2689d19d5b23 # +# ================================================================================== # +# Rule summary +# ---------------------------------------------------------------------------------- # +# Comment & mark 90-day old issues as stale +# Close stalled issues after 7 days +# Comment & mark 60-day old PR as stale +# Close stalled PRs after 10 days +# Any issue/pr with a planned milestone is exempt +# Issues/PRs with a confirmed label are exempt +# ================================================================================== # +# See repo url for descriptions of settings + +# ALL +staleLabel: stale +limitPerRun: 30 # numActions/hour + + +issues: + daysUntilStale: 90 + daysUntilClose: 7 + exemptProjects: false + exemptMilestones: true + exemptAssignees: false + exemptLabels: + - confirmed + - help-wanted + markComment: > + This issue is stale because it has been open 90 days with no recent activity. + It will be closed in 7 days, if no further activity occurs. Thank you for + your contributions. + unmarkComment: > + Thank you for the update and contribution! The stale label has been removed + and this issue will not be closed at this time. + closeComment: > + This issue was closed because activity was dormant for 97 days. + + +pulls: + daysUntilStale: 60 + daysUntilClose: 10 + exemptProjects: false + exemptMilestones: true + exemptAssignees: false + exemptLabels: + - confirmed + # Ignore PRs opened by Dependabot + - dependencies + markComment: > + This PR is stale because it has been open 60 days with no recent activity. + It will be closed in 10 days, if no further activity occurs. Thank you for + your contributions. + unmarkComment: > + Thank you for the update and contribution! The stale label has been removed + and this PR will not be closed at this time. + closeComment: > + This PR was closed because activity was dormant for 70 days. From d2904b250b1285017316575d5effeb42b4440dfe Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 13:48:07 -0500 Subject: [PATCH 120/167] test(fixtures): add common fixture to write default changelog from repo definition --- tests/const.py | 5 ++++ tests/fixtures/git_repo.py | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/tests/const.py b/tests/const.py index d53ddd967..7108f892a 100644 --- a/tests/const.py +++ b/tests/const.py @@ -1,3 +1,5 @@ +from datetime import datetime + A_FULL_VERSION_STRING = "1.11.567" A_PRERELEASE_VERSION_STRING = "2.3.4-dev.23" A_FULL_VERSION_STRING_WITH_BUILD_METADATA = "4.2.3+build.12345" @@ -6,6 +8,9 @@ EXAMPLE_REPO_NAME = "example_repo" EXAMPLE_HVCS_DOMAIN = "example.com" +TODAY_DATE_STR = datetime.now().strftime("%Y-%m-%d") +"""Date formatted as how it would appear in the changelog (Must match local timezone)""" + COMMIT_MESSAGE = "{version}\n\nAutomatically generated by python-semantic-release\n" # Different in-scope commits that produce a certain release type diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 7c55b5a2c..e06a4ea13 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -11,6 +11,7 @@ EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, + TODAY_DATE_STR, ) from tests.util import ( add_text_to_file, @@ -103,6 +104,15 @@ class GetRepoDefinitionFn(Protocol): def __call__(self, commit_type: CommitConvention = "angular") -> RepoDefinition: ... + class SimulateDefaultChangelogCreationFn(Protocol): + def __call__( + self, + repo_definition: RepoDefinition, + dest_file: Path | None = None, + tag_format: str = ... + ) -> str: + ... + @pytest.fixture(scope="session") def commit_author(): @@ -314,6 +324,56 @@ def _build_configured_base_repo( return _build_configured_base_repo +@pytest.fixture(scope="session") +def simulate_default_changelog_creation( + default_tag_format_str: str, +) -> SimulateDefaultChangelogCreationFn: + def build_version_entry(version: VersionStr, version_def: RepoVersionDef, tag_format: str) -> str: + version_entry = [] + if version == "Unreleased": + version_entry.append(f"## {version}\n") + else: + version_entry.append( + # TODO: artificial newline in front due to template when no Unreleased changes exist + f"\n## {tag_format.format(version=version)} ({TODAY_DATE_STR})\n" + ) + + for section_def in version_def["changelog_sections"]: + version_entry.append(f"### {section_def['section']}\n") + for i in section_def["i_commits"]: + version_entry.append(f"* {version_def['commits'][i]}\n") + + return str.join("\n", version_entry) + + def _mimic_semantic_release_default_changelog( + repo_definition: RepoDefinition, + dest_file: Path | None = None, + tag_format: str = default_tag_format_str, + ) -> str: + header = "# CHANGELOG" + version_entries = [] + + for version, version_def in repo_definition.items(): + # prepend entries to force reverse ordering + version_entries.insert( + 0, build_version_entry(version, version_def, tag_format) + ) + + changelog_content = str.join("\n" * 3, [ + header, + str.join("\n", [ + entry for entry in version_entries + ]) + ]) + + if dest_file is not None: + dest_file.write_text(changelog_content) + + return changelog_content + + return _mimic_semantic_release_default_changelog + + @pytest.fixture def example_project_git_repo( example_project_dir: ExProjectDir, From 3f4ff05c71d261b825d117ecd030d34e22009731 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 1 Jan 2024 18:26:27 -0500 Subject: [PATCH 121/167] test(cli-changelog): refactor changelog re-gen test to show debuggable results --- tests/command_line/test_changelog.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 0f7a4de05..d044159df 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -114,14 +114,10 @@ def test_changelog_noop_is_noop( ) def test_changelog_content_regenerated( repo: Repo, - tmp_path_factory: pytest.TempPathFactory, - example_project_dir: ExProjectDir, example_changelog_md: Path, cli_runner: CliRunner, ): - tempdir = tmp_path_factory.mktemp("test_changelog") - remove_dir_tree(tempdir.resolve(), force=True) - shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) + expected_changelog_content = example_changelog_md.read_text() # Remove the changelog and then check that we can regenerate it os.remove(str(example_changelog_md.resolve())) @@ -129,10 +125,10 @@ def test_changelog_content_regenerated( result = cli_runner.invoke(main, [changelog.name or "changelog"]) assert result.exit_code == 0 - dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) + actual_content = example_changelog_md.read_text() - differing_files = flatten_dircmp(dcmp) - assert not differing_files + # Check that the changelog content is the same as before + assert expected_changelog_content == actual_content # Just need to test that it works for "a" project, not all @@ -189,12 +185,12 @@ def test_changelog_post_to_release( ) as mocker, monkeypatch.context() as m: m.delenv("GITHUB_REPOSITORY", raising=False) m.delenv("CI_PROJECT_NAMESPACE", raising=False) - result = cli_runner.invoke(main, [changelog.name, *args]) + result = cli_runner.invoke(main, [changelog.name or "changelog", *args]) assert result.exit_code == 0 assert mocker.called - assert mock_adapter.called + assert mock_adapter.called and mock_adapter.last_request is not None assert mock_adapter.last_request.url == ( "https://{api_url}/repos/{owner}/{repo_name}/releases".format( api_url=Github.DEFAULT_API_DOMAIN, @@ -220,11 +216,15 @@ def test_custom_release_notes_template( # Arrange release_history = get_release_history_from_context(runtime_context_with_tags) tag = runtime_context_with_tags.repo.tags[-1].name + version = runtime_context_with_tags.version_translator.from_tag(tag) + if version is None: + raise ValueError(f"Tag {tag} not in release history") + release = release_history.released[version] # Act - resp = cli_runner.invoke(main, [changelog.name, "--post-to-release-tag", tag]) + resp = cli_runner.invoke(main, [changelog.name or "changelog", "--post-to-release-tag", tag]) expected_release_notes = runtime_context_with_tags.template_environment.from_string( EXAMPLE_RELEASE_NOTES_TEMPLATE ).render(version=version, release=release) @@ -235,5 +235,5 @@ def test_custom_release_notes_template( f"'semantic-release {changelog.name} --post-to-release-tag {tag}': " + resp.stderr ) - assert post_mocker.call_count == 1 + assert post_mocker.call_count == 1 and post_mocker.last_request is not None assert expected_release_notes == post_mocker.last_request.json()["body"] From 5556c3a881bfcc206df8adb21075b532a5551053 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 7 Jan 2024 00:23:36 -0500 Subject: [PATCH 122/167] test(changelog): add assertion to check for changelog file exist before read --- tests/command_line/test_changelog.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index d044159df..c7cfbf2dd 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -125,6 +125,9 @@ def test_changelog_content_regenerated( result = cli_runner.invoke(main, [changelog.name or "changelog"]) assert result.exit_code == 0 + # Check that the changelog file was re-created + assert example_changelog_md.exists() + actual_content = example_changelog_md.read_text() # Check that the changelog content is the same as before From 0b0778617b8e0d73365f5b223270625227c3ca30 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 2 Jan 2024 01:02:03 -0500 Subject: [PATCH 123/167] test: improve reliability & error readability of assertions --- tests/command_line/test_version.py | 15 ++++++----- tests/scenario/test_release_history.py | 36 ++++++++++++++------------ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 6f62e24fd..5c3ca8854 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -423,13 +423,13 @@ def test_version_no_push_force_level( assert head_before in repo.head.commit.parents dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) - differing_files = flatten_dircmp(dcmp) + differing_files = sorted(flatten_dircmp(dcmp)) # Changelog already reflects changes this should introduce - assert differing_files == [ + assert differing_files == sorted([ "pyproject.toml", f"src/{EXAMPLE_PROJECT_NAME}/_version.py", - ] + ]) # Compare pyproject.toml new_pyproject_toml = tomlkit.loads( @@ -699,13 +699,16 @@ def test_version_only_update_files_no_git_actions( ) dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) - differing_files = flatten_dircmp(dcmp) + differing_files = sorted(flatten_dircmp(dcmp)) # Files that should receive version change - assert differing_files == [ + expected_changed_files = sorted([ + # CHANGELOG.md is not included as no modification to Git History + # (no commit or tag) has been made "pyproject.toml", f"src/{EXAMPLE_PROJECT_NAME}/_version.py", - ] + ]) + assert expected_changed_files == differing_files # Compare pyproject.toml new_pyproject_toml = tomlkit.loads( diff --git a/tests/scenario/test_release_history.py b/tests/scenario/test_release_history.py index f71cb7396..42c1e2f18 100644 --- a/tests/scenario/test_release_history.py +++ b/tests/scenario/test_release_history.py @@ -233,15 +233,15 @@ def test_release_history( ), ) for k in expected_release_history.released: - expected, actual = expected_release_history.released[k], released[k]["elements"] - actual_released_messages = [ - res.commit.message for results in actual.values() for res in results - ] - assert all( - msg in actual_released_messages - for bucket in expected.values() - for msg in bucket - ) + expected = expected_release_history.released[k] + actual = released[k]["elements"] + actual_released_messages = str.join("\n\n", sorted([ + str(res.commit.message) for results in actual.values() for res in results + ])) + expected_released_messages = str.join("\n\n", sorted([ + msg for bucket in expected.values() for msg in bucket + ])) + assert expected_released_messages == actual_released_messages for commit_message in ANGULAR_COMMITS_MINOR: add_text_to_file(repo, file_in_repo) @@ -251,17 +251,21 @@ def test_release_history( new_unreleased, new_released = ReleaseHistory.from_git_history( repo, translator, default_angular_parser ) - actual_unreleased_messages = [ - res.commit.message for results in new_unreleased.values() for res in results - ] - assert all( - msg in actual_unreleased_messages + + actual_unreleased_messages = str.join("\n\n", sorted([ + str(res.commit.message) for results in new_unreleased.values() for res in results + ])) + + expected_unreleased_messages = str.join("\n\n", sorted([ + msg for bucket in [ + ANGULAR_COMMITS_MINOR[::-1], *expected_release_history.unreleased.values(), - ANGULAR_COMMITS_MINOR, ] for msg in bucket - ) + ])) + + assert expected_unreleased_messages == actual_unreleased_messages assert ( new_released == released ), "something that shouldn't be considered release has been released" From 8d119df1b0ca04f4e06446c608a2322497b30aac Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 2 Jan 2024 01:02:55 -0500 Subject: [PATCH 124/167] test(cli-version): ensure CHANGELOG is included in changed files --- tests/command_line/test_version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 5c3ca8854..f908f7940 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -427,6 +427,7 @@ def test_version_no_push_force_level( # Changelog already reflects changes this should introduce assert differing_files == sorted([ + "CHANGELOG.md", "pyproject.toml", f"src/{EXAMPLE_PROJECT_NAME}/_version.py", ]) From 7af8373c3612e6aeec19c52c006187faa418b5be Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 23:53:46 -0500 Subject: [PATCH 125/167] test(fixtures): remove changelog generation prevention --- tests/fixtures/example_project.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 35d7b7c1b..8d2bfab4c 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -111,7 +111,6 @@ def cached_example_project( setup_py_file: Path, changelog_md_file: Path, cached_files_dir: Path, - changelog_template_dir: Path, teardown_cached_dir: TeardownCachedDirFn, ) -> Path: """ @@ -162,11 +161,6 @@ def hello_world() -> None: # write file contents abs_filepath.write_text(contents) - # create the changelog template directory - cached_project_path.joinpath(changelog_template_dir).mkdir( - parents=True, exist_ok=True - ) - # trigger automatic cleanup of cache directory during teardown return teardown_cached_dir(cached_project_path) From 2a89f68bf14f540234cd218e3aab1c9e12707f21 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 23:56:13 -0500 Subject: [PATCH 126/167] test(fixtures): trigger changelog generation in repo fixtures --- tests/fixtures/git_repo.py | 12 ++++-------- .../repos/git_flow/repo_w_2_release_channels.py | 9 +++++++++ .../repos/git_flow/repo_w_3_release_channels.py | 9 +++++++++ .../repos/github_flow/repo_w_release_channels.py | 9 +++++++++ .../fixtures/repos/trunk_based_dev/repo_w_no_tags.py | 9 +++++++++ .../repos/trunk_based_dev/repo_w_prereleases.py | 9 +++++++++ tests/fixtures/repos/trunk_based_dev/repo_w_tags.py | 9 +++++++++ 7 files changed, 58 insertions(+), 8 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index e06a4ea13..208a63e86 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -109,7 +109,6 @@ def __call__( self, repo_definition: RepoDefinition, dest_file: Path | None = None, - tag_format: str = ... ) -> str: ... @@ -325,17 +324,15 @@ def _build_configured_base_repo( @pytest.fixture(scope="session") -def simulate_default_changelog_creation( - default_tag_format_str: str, -) -> SimulateDefaultChangelogCreationFn: - def build_version_entry(version: VersionStr, version_def: RepoVersionDef, tag_format: str) -> str: +def simulate_default_changelog_creation() -> SimulateDefaultChangelogCreationFn: + def build_version_entry(version: VersionStr, version_def: RepoVersionDef) -> str: version_entry = [] if version == "Unreleased": version_entry.append(f"## {version}\n") else: version_entry.append( # TODO: artificial newline in front due to template when no Unreleased changes exist - f"\n## {tag_format.format(version=version)} ({TODAY_DATE_STR})\n" + f"\n## v{version} ({TODAY_DATE_STR})\n" ) for section_def in version_def["changelog_sections"]: @@ -348,7 +345,6 @@ def build_version_entry(version: VersionStr, version_def: RepoVersionDef, tag_fo def _mimic_semantic_release_default_changelog( repo_definition: RepoDefinition, dest_file: Path | None = None, - tag_format: str = default_tag_format_str, ) -> str: header = "# CHANGELOG" version_entries = [] @@ -356,7 +352,7 @@ def _mimic_semantic_release_default_changelog( for version, version_def in repo_definition.items(): # prepend entries to force reverse ordering version_entries.insert( - 0, build_version_entry(version, version_def, tag_format) + 0, build_version_entry(version, version_def) ) changelog_content = str.join("\n" * 3, [ diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 29acb7c9e..1f78d7f4d 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -223,7 +224,9 @@ def build_git_flow_repo_with_2_release_channels( get_commits_for_git_flow_repo_with_2_release_channels: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: """ @@ -378,6 +381,12 @@ def _build_git_flow_repo_with_2_release_channels( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a 2nd alpha prerelease (v1.2.0-alpha.2) on the feature branch create_release_tagged_commit(git_repo, next_version, tag_format) diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index e333b0615..5744b3d7d 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -218,7 +219,9 @@ def build_git_flow_repo_w_3_release_channels( get_commits_for_git_flow_repo_w_3_release_channels: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_git_flow_repo_w_3_release_channels( @@ -373,6 +376,12 @@ def _build_git_flow_repo_w_3_release_channels( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a 3rd alpha prerelease (v1.1.0-alpha.3) create_release_tagged_commit(git_repo, next_version, tag_format) diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 0b2c2cd97..2b696c3f8 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -156,7 +157,9 @@ def build_github_flow_repo_w_feature_release_channel( get_commits_for_github_flow_repo_w_feature_release_channel: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_github_flow_repo_w_feature_release_channel( @@ -255,6 +258,12 @@ def _build_github_flow_repo_w_feature_release_channel( git_repo, next_version_def["commits"], hvcs ) + # Write the expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a feature level beta release (v0.3.0-beta.1) create_release_tagged_commit(git_repo, next_version, tag_format) diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 56782f616..5ee445a1d 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -25,6 +25,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -125,7 +126,9 @@ def _get_versions_for_trunk_only_repo_w_no_tags() -> list[VersionStr]: def build_trunk_only_repo_w_no_tags( get_commits_for_trunk_only_repo_w_no_tags: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, ) -> BuildRepoFn: def _build_trunk_only_repo_w_no_tags( dest_dir: Path | str, @@ -155,6 +158,12 @@ def _build_trunk_only_repo_w_no_tags( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + return repo_dir, hvcs return _build_trunk_only_repo_w_no_tags diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index ef000809f..496f15940 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -138,7 +139,9 @@ def build_trunk_only_repo_w_prerelease_tags( get_commits_for_trunk_only_repo_w_prerelease_tags: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_trunk_only_repo_w_prerelease_tags( @@ -210,6 +213,12 @@ def _build_trunk_only_repo_w_prerelease_tags( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a full release create_release_tagged_commit(git_repo, next_version, tag_format) diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index 6d4ba14ee..4b933bd41 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -106,7 +107,9 @@ def build_trunk_only_repo_w_tags( get_commits_for_trunk_only_repo_w_tags: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_trunk_only_repo_w_tags( @@ -155,6 +158,12 @@ def _build_trunk_only_repo_w_tags( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a patch level release (v0.1.1) create_release_tagged_commit(git_repo, next_version, tag_format) From a2b2b8f2ddeedb79e60cdb90de7a1d981509e814 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 2 Jan 2024 00:59:24 -0500 Subject: [PATCH 127/167] test(repo-commits): fix angular syntax for scopes in commits --- .../git_flow/repo_w_2_release_channels.py | 10 ++++----- .../git_flow/repo_w_3_release_channels.py | 16 +++++++------- .../github_flow/repo_w_release_channels.py | 2 +- tests/scenario/test_release_history.py | 22 +++++++++---------- .../changelog/test_default_changelog.py | 10 ++++----- .../changelog/test_release_notes.py | 2 +- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 1f78d7f4d..4e03ebda4 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -108,7 +108,7 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition }, "commits": [ { - "angular": "feat: (dev) add some more text", + "angular": "feat(dev): add some more text", "emoji": ":sparkles: (dev) add some more text", "scipy": "ENH: (dev) add some more text", "tag": ":sparkles: (dev) add some more text", @@ -124,7 +124,7 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition }, "commits": [ { - "angular": "fix: (dev) add some more text", + "angular": "fix(dev): add some more text", "emoji": ":bug: (dev) add some more text", "scipy": "MAINT: (dev) add some more text", "tag": ":nut_and_bolt: (dev) add some more text", @@ -140,7 +140,7 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", @@ -171,13 +171,13 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", }, { - "angular": "fix: (feature) add some missing text", + "angular": "fix(feature): add some missing text", "emoji": ":bug: (feature) add some missing text", "scipy": "MAINT: (feature) add some missing text", "tag": ":nut_and_bolt: (feature) add some missing text", diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index 5744b3d7d..a10b57de3 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -108,7 +108,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "feat: (dev) add some more text", + "angular": "feat(dev): add some more text", "emoji": ":sparkles: (dev) add some more text", "scipy": "ENH: (dev) add some more text", "tag": ":sparkles: (dev) add some more text", @@ -124,7 +124,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "fix: (dev) add some more text", + "angular": "fix(dev): add some more text", "emoji": ":bug: (dev) add some more text", "scipy": "MAINT: (dev) add some more text", "tag": ":nut_and_bolt: (dev) add some more text", @@ -140,7 +140,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", @@ -156,7 +156,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", @@ -172,10 +172,10 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "fix: (feature) add some more text", - "emoji": ":bug: (feature) add some more text", - "scipy": "MAINT: (feature) add some more text", - "tag": ":nut_and_bolt: (feature) add some more text", + "angular": "fix(feature): add some missing text", + "emoji": ":bug: (feature) add some missing text", + "scipy": "MAINT: (feature) add some missing text", + "tag": ":nut_and_bolt: (feature) add some missing text", }, ], }, diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 2b696c3f8..5c84162c7 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -108,7 +108,7 @@ def get_commits_for_github_flow_repo_w_feature_release_channel() -> GetRepoDefin }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", diff --git a/tests/scenario/test_release_history.py b/tests/scenario/test_release_history.py index 42c1e2f18..ce73e8ffb 100644 --- a/tests/scenario/test_release_history.py +++ b/tests/scenario/test_release_history.py @@ -93,7 +93,7 @@ class FakeReleaseHistoryElements(NamedTuple): "unknown": [COMMIT_MESSAGE.format(version="0.2.0")], }, Version.parse("0.3.0-beta.1"): { - "feature": ["feat: (feature) add some more text\n"], + "feature": ["feat(feature): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="0.3.0-beta.1")], }, }, @@ -119,20 +119,20 @@ class FakeReleaseHistoryElements(NamedTuple): "unknown": [COMMIT_MESSAGE.format(version="1.0.0")], }, Version.parse("1.1.0"): { - "feature": ["feat: (dev) add some more text\n"], + "feature": ["feat(dev): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0")], }, Version.parse("1.1.1"): { - "fix": ["fix: (dev) add some more text\n"], + "fix": ["fix(dev): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.1")], }, Version.parse("1.2.0-alpha.1"): { - "feature": ["feat: (feature) add some more text\n"], + "feature": ["feat(feature): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.2.0-alpha.1")], }, Version.parse("1.2.0-alpha.2"): { - "feature": ["feat: (feature) add some more text\n"], - "fix": ["fix: (feature) add some missing text\n"], + "feature": ["feat(feature): add some more text\n"], + "fix": ["fix(feature): add some missing text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.2.0-alpha.2")], }, }, @@ -158,23 +158,23 @@ class FakeReleaseHistoryElements(NamedTuple): "unknown": [COMMIT_MESSAGE.format(version="1.0.0")], }, Version.parse("1.1.0-rc.1"): { - "feature": ["feat: (dev) add some more text\n"], + "feature": ["feat(dev): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-rc.1")], }, Version.parse("1.1.0-rc.2"): { - "fix": ["fix: (dev) add some more text\n"], + "fix": ["fix(dev): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-rc.2")], }, Version.parse("1.1.0-alpha.1"): { - "feature": ["feat: (feature) add some more text\n"], + "feature": ["feat(feature): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-alpha.1")], }, Version.parse("1.1.0-alpha.2"): { - "feature": ["feat: (feature) add some more text\n"], + "feature": ["feat(feature): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-alpha.2")], }, Version.parse("1.1.0-alpha.3"): { - "fix": ["fix: (feature) add some more text\n"], + "fix": ["fix(feature): add some missing text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-alpha.3")], }, }, diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index e79fe323b..6867ac43d 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -28,27 +28,27 @@ def _cm_rstripped(version: str) -> str: # CHANGELOG ## v1.1.0-alpha.3 ({today_as_str}) ### Fix -* fix: (feature) add some more text +* fix(feature): add some missing text ### Unknown * {_cm_rstripped("1.1.0-alpha.3")} ## v1.1.0-alpha.2 ({today_as_str}) ### Feature -* feat: (feature) add some more text +* feat(feature): add some more text ### Unknown * {_cm_rstripped("1.1.0-alpha.2")} ## v1.1.0-alpha.1 ({today_as_str}) ### Feature -* feat: (feature) add some more text +* feat(feature): add some more text ### Unknown * {_cm_rstripped("1.1.0-alpha.1")} ## v1.1.0-rc.2 ({today_as_str}) ### Fix -* fix: (dev) add some more text +* fix(dev): add some more text ### Unknown * {_cm_rstripped("1.1.0-rc.2")} ## v1.1.0-rc.1 ({today_as_str}) ### Feature -* feat: (dev) add some more text +* feat(dev): add some more text ### Unknown * {_cm_rstripped("1.1.0-rc.1")} ## v1.0.0 ({today_as_str}) diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index 441522612..f7c67be64 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -27,7 +27,7 @@ def _cm_rstripped(version: str) -> str: EXPECTED_CONTENT = f"""\ # v1.1.0-alpha.3 ({today_as_str}) ## Fix -* fix: (feature) add some more text +* fix(feature): add some missing text ## Unknown * {_cm_rstripped("1.1.0-alpha.3")} """ From ebb5ca3ea208c15170f25376e749e61e3475d2ee Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 Feb 2024 09:56:08 -0500 Subject: [PATCH 128/167] test(unit-changelog): drop context test & duplicate/incorrect template Drop the test related changelog template as that is one more thing to maintain and does not actually match the embedded template provided to the user. Secondly, drop the context unit test as it does not provide value that the template ultimately will fail in other unit tests. --- .../changelog/TEST_CHANGELOG.md.j2 | 24 --- .../changelog/test_changelog_context.py | 144 ------------------ 2 files changed, 168 deletions(-) delete mode 100644 tests/unit/semantic_release/changelog/TEST_CHANGELOG.md.j2 delete mode 100644 tests/unit/semantic_release/changelog/test_changelog_context.py diff --git a/tests/unit/semantic_release/changelog/TEST_CHANGELOG.md.j2 b/tests/unit/semantic_release/changelog/TEST_CHANGELOG.md.j2 deleted file mode 100644 index e28faed21..000000000 --- a/tests/unit/semantic_release/changelog/TEST_CHANGELOG.md.j2 +++ /dev/null @@ -1,24 +0,0 @@ -{# - NOTE: this changelog test doesn't include commit hashes from the default template as - they always change - which makes it notoriously difficult to check exact content -#}# CHANGELOG -{% if context.history.unreleased | length > 0 %} -## Unreleased -{% for type_, commits in context.history.unreleased | dictsort %} -### {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} -{% else %} -* {{ commit.message.rstrip() }} -{% endif %}{% endfor %} -{% endfor %}{% endif %} -{% for version, release in context.history.released.items() %} -## {{ version.as_semver_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }}) -{% for type_, commits in release["elements"] | dictsort %} -### {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} -{% else %} -* {{ commit.message.rstrip() }} -{% endif %}{% endfor %} -{% endfor %}{% endfor %} diff --git a/tests/unit/semantic_release/changelog/test_changelog_context.py b/tests/unit/semantic_release/changelog/test_changelog_context.py deleted file mode 100644 index dcb62124c..000000000 --- a/tests/unit/semantic_release/changelog/test_changelog_context.py +++ /dev/null @@ -1,144 +0,0 @@ -import pytest -from git.objects.base import Object -from pytest_lazyfixture import lazy_fixture - -from semantic_release.changelog import environment, make_changelog_context -from semantic_release.changelog.release_history import ReleaseHistory -from semantic_release.hvcs import Bitbucket, Gitea, Github, Gitlab -from semantic_release.version.translator import VersionTranslator - -NULL_HEX_SHA = Object.NULL_HEX_SHA -SHORT_SHA = NULL_HEX_SHA[:7] - - -# Test with just one project for the moment - can be expanded to all -# example projects later - -CHANGELOG_TEMPLATE = r""" -# CHANGELOG -{% if context.history.unreleased | length > 0 %} -## Unreleased -{% for type_, commits in context.history.unreleased.items() %} -### {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) -{% else %} -* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) -{% endif %}{% endfor %}{% endfor %}{% endif %} -{% for version, release in context.history.released.items() %} -## {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }}) -{% for type_, commits in release["elements"].items() %} -### {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) -{% else %} -* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) -{% endif %}{% endfor %}{% endfor %}{% endfor %} -""" # noqa: E501 - -EXPECTED_CHANGELOG_CONTENT_ANGULAR = r""" -# CHANGELOG -## v0.2.0 -### Feature -* feat: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.2.0-rc.1 -### Feature -* feat: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.1-rc.1 -### Fix -* fix: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.0 -### Unknown -* Initial commit ([`{SHORT_SHA}`]({commit_url})) -""" - - -EXPECTED_CHANGELOG_CONTENT_EMOJI = r""" -# CHANGELOG -## v0.2.0 -### :sparkles: -* :sparkles: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.2.0-rc.1 -### :sparkles: -* :sparkles: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.1-rc.1 -### :bug: -* :bug: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.0 -### Unknown -* Initial commit ([`{SHORT_SHA}`]({commit_url})) -""" - -EXPECTED_CHANGELOG_CONTENT_SCIPY = r""" -# CHANGELOG -## v0.2.0 -### ENH: -* ENH: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.2.0-rc.1 -### ENH: -* ENH: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.1-rc.1 -### MAINT: -* MAINT: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.0 -### Unknown -* Initial commit ([`{SHORT_SHA}`]({commit_url})) -""" - -EXPECTED_CHANGELOG_CONTENT_TAG = r""" -# CHANGELOG -## v0.2.0 -### :sparkles: -* :sparkles: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.2.0-rc.1 -### :sparkles: -* :sparkles: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.1-rc.1 -### :nut_and_bolt: -* :nut_and_bolt: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.0 -### Unknown -* Initial commit ([`{SHORT_SHA}`]({commit_url})) -""" - - -@pytest.mark.parametrize("changelog_template", (CHANGELOG_TEMPLATE,)) -@pytest.mark.parametrize( - "repo, commit_parser, expected_changelog", - [ - ( - lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("default_angular_parser"), - EXPECTED_CHANGELOG_CONTENT_ANGULAR, - ), - ( - lazy_fixture("repo_with_single_branch_and_prereleases_emoji_commits"), - lazy_fixture("default_emoji_parser"), - EXPECTED_CHANGELOG_CONTENT_EMOJI, - ), - ( - lazy_fixture("repo_with_single_branch_and_prereleases_scipy_commits"), - lazy_fixture("default_scipy_parser"), - EXPECTED_CHANGELOG_CONTENT_SCIPY, - ), - ( - lazy_fixture("repo_with_single_branch_and_prereleases_tag_commits"), - lazy_fixture("default_tag_parser"), - EXPECTED_CHANGELOG_CONTENT_TAG, - ), - ], -) -@pytest.mark.parametrize("hvcs_client_class", (Github, Gitlab, Gitea, Bitbucket)) -@pytest.mark.usefixtures("expected_changelog") -def test_changelog_context(repo, changelog_template, commit_parser, hvcs_client_class): - # NOTE: this test only checks that the changelog can be rendered with the - # contextual information we claim to offer. Testing that templates render - # appropriately is the responsibility of the template engine's authors, - # so we shouldn't be re-testing that here. - hvcs_client = hvcs_client_class(remote_url=repo.remote().url) - env = environment(lstrip_blocks=True, keep_trailing_newline=True, trim_blocks=True) - rh = ReleaseHistory.from_git_history(repo, VersionTranslator(), commit_parser) - context = make_changelog_context(hvcs_client=hvcs_client, release_history=rh) - context.bind_to_environment(env) - actual_content = env.from_string(changelog_template).render() - assert actual_content From 620d62a60c47c3a8ffdc79cb74ee36ca330767e3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 Feb 2024 11:05:46 -0500 Subject: [PATCH 129/167] test(unit-release-notes): refactor template testing Drop the test related release notes template as that is one more thing to maintain and use the one provided to the user. --- .../changelog/test_release_notes.md.j2 | 8 -- .../changelog/test_release_notes.py | 118 +++++++++++++----- 2 files changed, 86 insertions(+), 40 deletions(-) delete mode 100644 tests/unit/semantic_release/changelog/test_release_notes.md.j2 diff --git a/tests/unit/semantic_release/changelog/test_release_notes.md.j2 b/tests/unit/semantic_release/changelog/test_release_notes.md.j2 deleted file mode 100644 index 088337c84..000000000 --- a/tests/unit/semantic_release/changelog/test_release_notes.md.j2 +++ /dev/null @@ -1,8 +0,0 @@ -# {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }}) -{% for type_, commits in release["elements"] | dictsort %} -## {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} -{% else %} -* {{ commit.message.rstrip() }} -{% endif %}{% endfor %}{% endfor %} diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index f7c67be64..cd2adf0e9 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -1,53 +1,107 @@ -# NOTE: use backport with newer API +from __future__ import annotations + from datetime import datetime +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest +from git import Commit, Repo +from git.objects import Object +# NOTE: use backport with newer API to support 3.7 from importlib_resources import files +import semantic_release from semantic_release.changelog.context import make_changelog_context -from semantic_release.changelog.release_history import ReleaseHistory +from semantic_release.changelog.release_history import Release, ReleaseHistory from semantic_release.changelog.template import environment -from semantic_release.hvcs import Github -from semantic_release.version import Version, VersionTranslator +from semantic_release.commit_parser import ParsedCommit +from semantic_release.enums import LevelBump +from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.version import Version -from tests.const import COMMIT_MESSAGE +from tests.const import TODAY_DATE_STR -default_release_notes_template = ( - files("tests") - .joinpath("unit/semantic_release/changelog/test_release_notes.md.j2") - .read_text(encoding="utf-8") -) +if TYPE_CHECKING: + from git import Actor -today_as_str = datetime.now().strftime("%Y-%m-%d") + from semantic_release.hvcs import HvcsBase -def _cm_rstripped(version: str) -> str: - return COMMIT_MESSAGE.format(version=version).rstrip() +@pytest.fixture +def artificial_release_history(commit_author: Actor): + version = Version.parse("1.1.0-alpha.3") + commit_subject = "fix(cli): fix a problem" + fix_commit = Commit( + Repo("."), + Object.NULL_HEX_SHA[:20].encode("utf-8"), + message=commit_subject, + ) -EXPECTED_CONTENT = f"""\ -# v1.1.0-alpha.3 ({today_as_str}) -## Fix -* fix(feature): add some missing text -## Unknown -* {_cm_rstripped("1.1.0-alpha.3")} -""" + fix_commit_parsed = ParsedCommit( + bump=LevelBump.PATCH, + type="fix", + scope="cli", + descriptions=[commit_subject], + breaking_descriptions=[], + commit=fix_commit, + ) + + return ReleaseHistory( + unreleased={}, + released={ + version: Release( + tagger=commit_author, + committer=commit_author, + tagged_date=datetime.utcnow(), + elements={ + "fix": [fix_commit_parsed], + } + ) + } + ) -def test_default_changelog_template( - repo_with_git_flow_and_release_channels_angular_commits, default_angular_parser +@pytest.fixture +def release_notes_template() -> str: + """Retrieve the semantic-release default release notes template.""" + version_notes_template = files(semantic_release.__name__).joinpath( + Path("data", "templates", "release_notes.md.j2") + ) + return version_notes_template.read_text(encoding="utf-8") + + +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +def test_default_release_notes_template( + example_git_https_url: str, + hvcs_client: type[HvcsBase], + release_notes_template: str, + artificial_release_history: ReleaseHistory, ): - version = Version.parse("1.1.0-alpha.3") - repo = repo_with_git_flow_and_release_channels_angular_commits + """ + Unit test goal: just make sure it renders the release notes template without error. + + Scenarios are better suited for all the variations (commit types). + """ + version_str = "1.1.0-alpha.3" + version = Version.parse(version_str) + commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] + commit_url = hvcs_client(example_git_https_url).commit_hash_url(commit_obj.commit.hexsha) + commit_description = str.join('\n', commit_obj.descriptions) + expected_content = str.join("\n", [ + f"# v{version_str} ({TODAY_DATE_STR})", + "## Fix", + f"* {commit_description} ([`{commit_obj.commit.hexsha[:7]}`]({commit_url}))", + "", + ]) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) - rh = ReleaseHistory.from_git_history( - repo=repo, translator=VersionTranslator(), commit_parser=default_angular_parser - ) context = make_changelog_context( - hvcs_client=Github(remote_url=repo.remote().url), release_history=rh + hvcs_client=hvcs_client(remote_url=example_git_https_url), + release_history=artificial_release_history, ) context.bind_to_environment(env) - release = rh.released[version] - actual_content = env.from_string(default_release_notes_template).render( - version=version, release=release + actual_content = env.from_string(release_notes_template).render( + version=version, release=context.history.released[version] ) - assert actual_content == EXPECTED_CONTENT + assert expected_content == actual_content From f8a718f7c00baa58521ffb21b231437652d1e102 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 Feb 2024 23:23:03 -0500 Subject: [PATCH 130/167] test(unit-changelog): refactor template testing to be fast & simple --- tests/command_line/test_changelog.py | 1 + .../changelog/test_default_changelog.py | 238 +++++++++++------- 2 files changed, 151 insertions(+), 88 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index c7cfbf2dd..bcff9d2ae 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -110,6 +110,7 @@ def test_changelog_noop_is_noop( lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), lazy_fixture("repo_with_git_flow_angular_commits"), lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), + lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits_using_tag_format"), ], ) def test_changelog_content_regenerated( diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index 6867ac43d..cc138b0de 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -1,109 +1,171 @@ -# NOTE: use backport with newer API +from __future__ import annotations + from datetime import datetime +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest +from git import Commit, Object, Repo +# NOTE: use backport with newer API from importlib_resources import files +import semantic_release from semantic_release.changelog.context import make_changelog_context -from semantic_release.changelog.release_history import ReleaseHistory +from semantic_release.changelog.release_history import Release, ReleaseHistory from semantic_release.changelog.template import environment -from semantic_release.hvcs import Github -from semantic_release.version.translator import VersionTranslator - -from tests.const import COMMIT_MESSAGE - -default_changelog_template = ( - files("tests") - .joinpath("unit/semantic_release/changelog/TEST_CHANGELOG.md.j2") - .read_text(encoding="utf-8") -) - -today_as_str = datetime.now().strftime("%Y-%m-%d") - - -def _cm_rstripped(version: str) -> str: - return COMMIT_MESSAGE.format(version=version).rstrip() - - -EXPECTED_CONTENT = f"""\ -# CHANGELOG -## v1.1.0-alpha.3 ({today_as_str}) -### Fix -* fix(feature): add some missing text -### Unknown -* {_cm_rstripped("1.1.0-alpha.3")} -## v1.1.0-alpha.2 ({today_as_str}) -### Feature -* feat(feature): add some more text -### Unknown -* {_cm_rstripped("1.1.0-alpha.2")} -## v1.1.0-alpha.1 ({today_as_str}) -### Feature -* feat(feature): add some more text -### Unknown -* {_cm_rstripped("1.1.0-alpha.1")} -## v1.1.0-rc.2 ({today_as_str}) -### Fix -* fix(dev): add some more text -### Unknown -* {_cm_rstripped("1.1.0-rc.2")} -## v1.1.0-rc.1 ({today_as_str}) -### Feature -* feat(dev): add some more text -### Unknown -* {_cm_rstripped("1.1.0-rc.1")} -## v1.0.0 ({today_as_str}) -### Feature -* feat: add some more text -### Unknown -* {_cm_rstripped("1.0.0")} -## v1.0.0-rc.1 ({today_as_str}) -### Breaking -* feat!: add some more text -### Unknown -* {_cm_rstripped("1.0.0-rc.1")} -## v0.1.1-rc.1 ({today_as_str}) -### Fix -* fix: add some more text -### Unknown -* {_cm_rstripped("0.1.1-rc.1")} -## v0.1.0 ({today_as_str}) -### Unknown -* {_cm_rstripped("0.1.0")} -* Initial commit -""" +from semantic_release.commit_parser import ParsedCommit +from semantic_release.enums import LevelBump +from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.version.translator import Version + +from tests.const import TODAY_DATE_STR + +if TYPE_CHECKING: + from git import Actor + + from semantic_release.hvcs import HvcsBase + + +@pytest.fixture +def default_changelog_template() -> str: + """Retrieve the semantic-release default changelog template.""" + version_notes_template = files(semantic_release.__name__).joinpath( + Path("data", "templates", "CHANGELOG.md.j2") + ) + return version_notes_template.read_text(encoding="utf-8") + + +@pytest.fixture +def artificial_release_history(commit_author: Actor): + version = Version.parse("1.0.0") + + commit_subject = "fix(cli): fix a problem" + + fix_commit = Commit( + Repo("."), + Object.NULL_HEX_SHA[:20].encode("utf-8"), + message=commit_subject, + ) + + fix_commit_parsed = ParsedCommit( + bump=LevelBump.PATCH, + type="fix", + scope="cli", + descriptions=[commit_subject], + breaking_descriptions=[], + commit=fix_commit, + ) + commit_subject = "feat(cli): add a new feature" + feat_commit = Commit( + Repo("."), + Object.NULL_HEX_SHA[:20].encode("utf-8"), + message=commit_subject, + ) + + feat_commit_parsed = ParsedCommit( + bump=LevelBump.MINOR, + type="feat", + scope="cli", + descriptions=[commit_subject], + breaking_descriptions=[], + commit=feat_commit, + ) + + return ReleaseHistory( + unreleased={ + "feature": [feat_commit_parsed], + }, + released={ + version: Release( + tagger=commit_author, + committer=commit_author, + tagged_date=datetime.utcnow(), + elements={ + "feature": [feat_commit_parsed], + "fix": [fix_commit_parsed], + } + ) + } + ) + + +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) def test_default_changelog_template( - repo_with_git_flow_and_release_channels_angular_commits, default_angular_parser + default_changelog_template: str, + hvcs_client: type[HvcsBase], + example_git_https_url: str, + artificial_release_history: ReleaseHistory, ): - repo = repo_with_git_flow_and_release_channels_angular_commits + version_str = "1.0.0" + version = Version.parse(version_str) + rh = artificial_release_history + rh.unreleased = {} # Wipe out unreleased + + feat_commit_obj = artificial_release_history.released[version]["elements"]["feature"][0] + feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url(feat_commit_obj.commit.hexsha) + feat_description = str.join('\n', feat_commit_obj.descriptions) + + fix_commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] + fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url(fix_commit_obj.commit.hexsha) + fix_description = str.join('\n', fix_commit_obj.descriptions) + + expected_changelog = str.join("\n", [ + "# CHANGELOG", + f"## v{version_str} ({TODAY_DATE_STR})", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "### Fix", + f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", + "", + ]) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) - rh = ReleaseHistory.from_git_history( - repo=repo, translator=VersionTranslator(), commit_parser=default_angular_parser - ) context = make_changelog_context( - hvcs_client=Github(remote_url=repo.remote().url), release_history=rh + hvcs_client=hvcs_client(remote_url=example_git_https_url), + release_history=rh, ) context.bind_to_environment(env) - actual_content = env.from_string(default_changelog_template).render() - assert actual_content == EXPECTED_CONTENT + actual_changelog = env.from_string(default_changelog_template).render() + assert expected_changelog == actual_changelog -def test_default_changelog_template_using_tag_format( - repo_with_git_flow_and_release_channels_angular_commits_using_tag_format, - default_angular_parser, +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +def test_default_changelog_template_w_unreleased_changes( + default_changelog_template: str, + hvcs_client: type[HvcsBase], + example_git_https_url: str, + artificial_release_history: ReleaseHistory, ): - repo = repo_with_git_flow_and_release_channels_angular_commits_using_tag_format + version_str = "1.0.0" + version = Version.parse(version_str) + + feat_commit_obj = artificial_release_history.released[version]["elements"]["feature"][0] + feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url(feat_commit_obj.commit.hexsha) + feat_description = str.join('\n', feat_commit_obj.descriptions) + + fix_commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] + fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url(fix_commit_obj.commit.hexsha) + fix_description = str.join('\n', fix_commit_obj.descriptions) + + expected_changelog = str.join("\n", [ + "# CHANGELOG", + "## Unreleased", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + f"## v{version_str} ({TODAY_DATE_STR})", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "### Fix", + f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", + "", + ]) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) - rh = ReleaseHistory.from_git_history( - repo=repo, - translator=VersionTranslator(tag_format="vpy{version}"), - commit_parser=default_angular_parser, - ) context = make_changelog_context( - hvcs_client=Github(remote_url=repo.remote().url), release_history=rh + hvcs_client=hvcs_client(remote_url=example_git_https_url), + release_history=artificial_release_history, ) context.bind_to_environment(env) - - actual_content = env.from_string(default_changelog_template).render() - assert actual_content == EXPECTED_CONTENT + actual_changelog = env.from_string(default_changelog_template).render() + assert expected_changelog == actual_changelog From 929b861a5d827913efac325f9db6ae7b2f44ef77 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 10 Feb 2024 00:05:04 -0500 Subject: [PATCH 131/167] test(fixtures): refactor for better chronological ordering for test success --- tests/scenario/test_template_render.py | 33 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/tests/scenario/test_template_render.py b/tests/scenario/test_template_render.py index af116a74f..63aef9514 100644 --- a/tests/scenario/test_template_render.py +++ b/tests/scenario/test_template_render.py @@ -1,11 +1,19 @@ +from __future__ import annotations + import itertools import os from pathlib import Path +from typing import TYPE_CHECKING import pytest from semantic_release.changelog.template import environment, recursive_render +if TYPE_CHECKING: + from tests.conftest import ExProjectDir + +# FIX me + NORMAL_TEMPLATE_SRC = """--- content: - a string @@ -40,6 +48,7 @@ def _strip_trailing_j2(path: Path) -> Path: @pytest.fixture def normal_template(example_project_template_dir): template = example_project_template_dir / "normal.yaml.j2" + template.parent.mkdir(parents=True, exist_ok=True) template.write_text(NORMAL_TEMPLATE_SRC) return template @@ -48,14 +57,14 @@ def normal_template(example_project_template_dir): def long_directory_path(example_project_template_dir): # NOTE: fixture enables using Path rather than # constant string, so no issue with / vs \ on Windows - d = example_project_template_dir / "long" / "dir" / "path" - os.makedirs(str(d.resolve()), exist_ok=True) - return d + folder = example_project_template_dir / "long" / "dir" / "path" + return folder @pytest.fixture def deeply_nested_file(long_directory_path): file = long_directory_path / "buried.txt" + file.parent.mkdir(parents=True, exist_ok=True) file.write_text(PLAINTEXT_FILE_CONTENT) return file @@ -63,20 +72,21 @@ def deeply_nested_file(long_directory_path): @pytest.fixture def hidden_file(example_project_template_dir): file = example_project_template_dir / ".hidden" + file.parent.mkdir(parents=True, exist_ok=True) file.write_text("I shouldn't be present") return file @pytest.fixture -def directory_path_with_hidden_subfolder(example_project_template_dir): - d = example_project_template_dir / "path" / ".subfolder" / "hidden" - os.makedirs(str(d.resolve()), exist_ok=True) - return d +def directory_path_with_hidden_subfolder(example_project_template_dir: Path) -> Path: + folder = example_project_template_dir / "path" / ".subfolder" / "hidden" + return folder @pytest.fixture def excluded_file(directory_path_with_hidden_subfolder): file = directory_path_with_hidden_subfolder / "excluded.txt" + file.parent.mkdir(parents=True, exist_ok=True) file.write_text("I shouldn't be present") return file @@ -132,15 +142,14 @@ def test_recursive_render( @pytest.fixture -def dotfolder_template_dir(example_project_dir: Path): - newpath = example_project_dir / ".templates/.psr-templates" - newpath.mkdir(parents=True, exist_ok=True) - return newpath +def dotfolder_template_dir(example_project_dir: ExProjectDir) -> Path: + return example_project_dir / ".templates/.psr-templates" @pytest.fixture -def dotfolder_template(dotfolder_template_dir: Path): +def dotfolder_template(init_example_project: None, dotfolder_template_dir: Path) -> Path: tmpl = dotfolder_template_dir / "template.txt" + tmpl.parent.mkdir(parents=True, exist_ok=True) tmpl.write_text("I am a template") return tmpl From 77c3816f79ad3c1ef9687304b4773e6485e4ee2f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 10 Feb 2024 00:09:33 -0500 Subject: [PATCH 132/167] test(changelog): enforce common single newline after generated docs --- tests/command_line/test_changelog.py | 2 +- tests/fixtures/git_repo.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index bcff9d2ae..4197ac002 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -231,7 +231,7 @@ def test_custom_release_notes_template( resp = cli_runner.invoke(main, [changelog.name or "changelog", "--post-to-release-tag", tag]) expected_release_notes = runtime_context_with_tags.template_environment.from_string( EXAMPLE_RELEASE_NOTES_TEMPLATE - ).render(version=version, release=release) + ).render(version=version, release=release) + '\n' # Assert assert resp.exit_code == 0, ( diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 208a63e86..4c5eba4fc 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -360,7 +360,7 @@ def _mimic_semantic_release_default_changelog( str.join("\n", [ entry for entry in version_entries ]) - ]) + ]).rstrip() + '\n' if dest_file is not None: dest_file.write_text(changelog_content) From 0b4a45e3673d0408016dc8e7b0dce98007a763e3 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 10 Feb 2024 00:10:27 -0500 Subject: [PATCH 133/167] fix(changelog): make sure default templates render ending in 1 newline --- semantic_release/cli/commands/changelog.py | 4 ++-- semantic_release/cli/commands/version.py | 2 +- semantic_release/cli/common.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/semantic_release/cli/commands/changelog.py b/semantic_release/cli/commands/changelog.py index 8623219dd..785f0a149 100644 --- a/semantic_release/cli/commands/changelog.py +++ b/semantic_release/cli/commands/changelog.py @@ -67,7 +67,7 @@ def changelog(ctx: click.Context, release_tag: str | None = None) -> None: ) else: changelog_text = render_default_changelog_file(env) - changelog_file.write_text(changelog_text, encoding="utf-8") + changelog_file.write_text(f"{changelog_text}\n", encoding="utf-8") else: if runtime.global_cli_options.noop: @@ -112,7 +112,7 @@ def changelog(ctx: click.Context, release_tag: str | None = None) -> None: else: try: hvcs_client.create_or_update_release( - release_tag, release_notes, prerelease=version.is_prerelease + release_tag, f"{release_notes}\n", prerelease=version.is_prerelease ) except Exception as e: log.exception(e) diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index e14e9e7b1..5a9bbb6d5 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -474,7 +474,7 @@ def version( # noqa: C901 ) else: changelog_text = render_default_changelog_file(env) - changelog_file.write_text(changelog_text, encoding="utf-8") + changelog_file.write_text(f"{changelog_text}\n", encoding="utf-8") updated_paths = [str(changelog_file.relative_to(repo.working_dir))] diff --git a/semantic_release/cli/common.py b/semantic_release/cli/common.py index 10b285ba9..c0ede5ac2 100644 --- a/semantic_release/cli/common.py +++ b/semantic_release/cli/common.py @@ -34,7 +34,7 @@ def render_default_changelog_file(template_environment: Environment) -> str: .read_text(encoding="utf-8") ) tmpl = template_environment.from_string(changelog_text) - return tmpl.render() + return tmpl.render().rstrip() def render_release_notes( @@ -45,4 +45,4 @@ def render_release_notes( ) -> str: return template_environment.from_string(release_notes_template).render( version=version, release=release - ) + ).rstrip() From 74c9dec2869c41700bbfe378f3f7833fa02a821c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 7 Jan 2024 00:45:11 -0500 Subject: [PATCH 134/167] style(tests): add additional typing to test args --- tests/command_line/test_changelog.py | 15 +++--- tests/command_line/test_main.py | 38 ++++++++------ tests/command_line/test_version.py | 52 ++++++++++++------- tests/scenario/test_template_render.py | 18 ++++--- .../unit/semantic_release/cli/test_config.py | 12 +++-- 5 files changed, 82 insertions(+), 53 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 4197ac002..abece8368 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -32,6 +32,9 @@ from tests.fixtures.example_project import ExProjectDir, UseReleaseNotesTemplateFn +changelog_subcmd = changelog.name or changelog.__name__ + + @pytest.mark.parametrize( "repo,tag", [ @@ -86,7 +89,7 @@ def test_changelog_noop_is_noop( return_value=session, ), requests_mock.Mocker(session=session) as mocker: result = cli_runner.invoke( - main, ["--noop", changelog.name or "changelog", *args] + main, ["--noop", changelog_subcmd, *args] ) assert result.exit_code == 0 @@ -123,7 +126,7 @@ def test_changelog_content_regenerated( # Remove the changelog and then check that we can regenerate it os.remove(str(example_changelog_md.resolve())) - result = cli_runner.invoke(main, [changelog.name or "changelog"]) + result = cli_runner.invoke(main, [changelog_subcmd]) assert result.exit_code == 0 # Check that the changelog file was re-created @@ -150,7 +153,7 @@ def test_changelog_release_tag_not_in_history( remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) - result = cli_runner.invoke(main, [changelog.name or "changelog", *args]) + result = cli_runner.invoke(main, [changelog_subcmd, *args]) assert result.exit_code == 2 assert "not in release history" in result.stderr.lower() @@ -189,7 +192,7 @@ def test_changelog_post_to_release( ) as mocker, monkeypatch.context() as m: m.delenv("GITHUB_REPOSITORY", raising=False) m.delenv("CI_PROJECT_NAMESPACE", raising=False) - result = cli_runner.invoke(main, [changelog.name or "changelog", *args]) + result = cli_runner.invoke(main, [changelog_subcmd, *args]) assert result.exit_code == 0 @@ -228,7 +231,7 @@ def test_custom_release_notes_template( release = release_history.released[version] # Act - resp = cli_runner.invoke(main, [changelog.name or "changelog", "--post-to-release-tag", tag]) + resp = cli_runner.invoke(main, [changelog_subcmd, "--post-to-release-tag", tag]) expected_release_notes = runtime_context_with_tags.template_environment.from_string( EXAMPLE_RELEASE_NOTES_TEMPLATE ).render(version=version, release=release) + '\n' @@ -236,7 +239,7 @@ def test_custom_release_notes_template( # Assert assert resp.exit_code == 0, ( "Unexpected failure in command " - f"'semantic-release {changelog.name} --post-to-release-tag {tag}': " + f"'semantic-release {changelog_subcmd} --post-to-release-tag {tag}': " + resp.stderr ) assert post_mocker.call_count == 1 and post_mocker.last_request is not None diff --git a/tests/command_line/test_main.py b/tests/command_line/test_main.py index 625fd4b7d..4b47facd8 100644 --- a/tests/command_line/test_main.py +++ b/tests/command_line/test_main.py @@ -1,5 +1,8 @@ +from __future__ import annotations + import json import os +from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING @@ -10,24 +13,29 @@ from semantic_release.cli import main if TYPE_CHECKING: + from pathlib import Path + from click.testing import CliRunner + from git import Repo from tests.fixtures.example_project import UpdatePyprojectTomlFn -def test_main_prints_version_and_exits(cli_runner): +def test_main_prints_version_and_exits(cli_runner: CliRunner): result = cli_runner.invoke(main, ["--version"]) assert result.exit_code == 0 assert result.output == f"semantic-release, version {__version__}\n" @pytest.mark.parametrize("args", [[], ["--help"]]) -def test_main_prints_help_text(cli_runner, args): +def test_main_prints_help_text(cli_runner: CliRunner, args: list[str]): result = cli_runner.invoke(main, args) assert result.exit_code == 0 -def test_not_a_release_branch_exit_code(repo_with_git_flow_angular_commits, cli_runner): +def test_not_a_release_branch_exit_code( + repo_with_git_flow_angular_commits: Repo, cli_runner: CliRunner +): # Run anything that doesn't trigger the help text repo_with_git_flow_angular_commits.git.checkout("-b", "branch-does-not-exist") result = cli_runner.invoke(main, ["version", "--no-commit"]) @@ -35,7 +43,7 @@ def test_not_a_release_branch_exit_code(repo_with_git_flow_angular_commits, cli_ def test_not_a_release_branch_exit_code_with_strict( - repo_with_git_flow_angular_commits, cli_runner + repo_with_git_flow_angular_commits: Repo, cli_runner: CliRunner ): # Run anything that doesn't trigger the help text repo_with_git_flow_angular_commits.git.checkout("-b", "branch-does-not-exist") @@ -44,7 +52,7 @@ def test_not_a_release_branch_exit_code_with_strict( def test_not_a_release_branch_detached_head_exit_code( - repo_with_git_flow_angular_commits, cli_runner + repo_with_git_flow_angular_commits: Repo, cli_runner: CliRunner ): expected_err_msg = ( "Detached HEAD state cannot match any release groups; no release will be made" @@ -60,7 +68,7 @@ def test_not_a_release_branch_detached_head_exit_code( @pytest.fixture -def toml_file_with_no_configuration_for_psr(tmp_path): +def toml_file_with_no_configuration_for_psr(tmp_path: Path) -> Path: path = tmp_path / "config.toml" path.write_text( dedent( @@ -76,7 +84,7 @@ def toml_file_with_no_configuration_for_psr(tmp_path): @pytest.fixture -def json_file_with_no_configuration_for_psr(tmp_path): +def json_file_with_no_configuration_for_psr(tmp_path: Path) -> Path: path = tmp_path / "config.json" path.write_text(json.dumps({"foo": [1, 2, 3]})) @@ -85,8 +93,8 @@ def json_file_with_no_configuration_for_psr(tmp_path): @pytest.mark.usefixtures("repo_with_git_flow_angular_commits") def test_default_config_is_used_when_none_in_toml_config_file( - cli_runner, - toml_file_with_no_configuration_for_psr, + cli_runner: CliRunner, + toml_file_with_no_configuration_for_psr: Path, ): result = cli_runner.invoke( main, @@ -98,8 +106,8 @@ def test_default_config_is_used_when_none_in_toml_config_file( @pytest.mark.usefixtures("repo_with_git_flow_angular_commits") def test_default_config_is_used_when_none_in_json_config_file( - cli_runner, - json_file_with_no_configuration_for_psr, + cli_runner: CliRunner, + json_file_with_no_configuration_for_psr: Path, ): result = cli_runner.invoke( main, @@ -111,7 +119,7 @@ def test_default_config_is_used_when_none_in_json_config_file( @pytest.mark.usefixtures("repo_with_git_flow_angular_commits") def test_errors_when_config_file_does_not_exist_and_passed_explicitly( - cli_runner, + cli_runner: CliRunner, ): result = cli_runner.invoke( main, @@ -124,7 +132,7 @@ def test_errors_when_config_file_does_not_exist_and_passed_explicitly( @pytest.mark.usefixtures("repo_with_no_tags_angular_commits") def test_errors_when_config_file_invalid_configuration( - cli_runner: "CliRunner", update_pyproject_toml: "UpdatePyprojectTomlFn" + cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn ): update_pyproject_toml("tool.semantic_release.remote.type", "invalidType") result = cli_runner.invoke(main, ["--config", "pyproject.toml", "version"]) @@ -136,8 +144,8 @@ def test_errors_when_config_file_invalid_configuration( def test_uses_default_config_when_no_config_file_found( - tmp_path, - cli_runner, + tmp_path: Path, + cli_runner: CliRunner, ): # We have to initialise an empty git repository, as the example projects # all have pyproject.toml configs which would be used by default diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index f908f7940..2b86dc354 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -38,6 +38,9 @@ ) +version_subcmd = version.name or version.__name__ + + @pytest.mark.parametrize( "repo", [ @@ -49,7 +52,12 @@ lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], ) -def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_runner): +def test_version_noop_is_noop( + tmp_path_factory: pytest.TempPathFactory, + example_project_dir: ExProjectDir, + repo: Repo, + cli_runner: CliRunner, +): # Make a commit to ensure we have something to release # otherwise the "no release will be made" logic will kick in first new_file = example_project_dir / "temp.txt" @@ -65,7 +73,7 @@ def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_r head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) - result = cli_runner.invoke(main, ["--noop", version.name]) + result = cli_runner.invoke(main, ["--noop", version_subcmd]) tags_after = sorted(repo.tags, key=lambda tag: tag.name) head_after = repo.head.commit @@ -216,7 +224,12 @@ def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_r ], ) def test_version_print( - repo, cli_args, expected_stdout, example_project_dir, tmp_path_factory, cli_runner + repo: Repo, + cli_args: list[str], + expected_stdout: str, + example_project_dir: ExProjectDir, + tmp_path_factory: pytest.TempPathFactory, + cli_runner: CliRunner, ): # Make a commit to ensure we have something to release # otherwise the "no release will be made" logic will kick in first @@ -232,7 +245,7 @@ def test_version_print( head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) - result = cli_runner.invoke(main, [version.name, *cli_args, "--print"]) + result = cli_runner.invoke(main, [version_subcmd, *cli_args, "--print"]) tags_after = sorted(repo.tags, key=lambda tag: tag.name) head_after = repo.head.commit @@ -258,11 +271,11 @@ def test_version_print( lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], ) -def test_version_already_released_no_push(repo, cli_runner): +def test_version_already_released_no_push(repo: Repo, cli_runner: CliRunner): # In these tests, unless arguments are supplied then the latest version # has already been released, so we expect an exit code of 2 with the message # to indicate that no release will be made - result = cli_runner.invoke(main, ["--strict", version.name, "--no-push"]) + result = cli_runner.invoke(main, ["--strict", version_subcmd, "--no-push"]) assert result.exit_code == 2 assert "no release will be made" in result.stderr.lower() @@ -410,7 +423,7 @@ def test_version_no_push_force_level( tags_before = sorted(repo.tags, key=lambda tag: tag.name) result = cli_runner.invoke( - main, [version.name or "version", *cli_args, "--no-push"] + main, [version_subcmd or "version", *cli_args, "--no-push"] ) tags_after = sorted(repo.tags, key=lambda tag: tag.name) @@ -481,17 +494,16 @@ def test_version_no_push_force_level( ], ) def test_version_build_metadata_triggers_new_version(repo: Repo, cli_runner: CliRunner): - version_cmd_name = version.name or "version" # Verify we get "no version to release" without build metadata no_metadata_result = cli_runner.invoke( - main, ["--strict", version_cmd_name, "--no-push"] + main, ["--strict", version_subcmd, "--no-push"] ) assert no_metadata_result.exit_code == 2 assert "no release will be made" in no_metadata_result.stderr.lower() metadata_suffix = "build.abc-12345" result = cli_runner.invoke( - main, [version_cmd_name, "--no-push", "--build-metadata", metadata_suffix] + main, [version_subcmd, "--no-push", "--build-metadata", metadata_suffix] ) assert result.exit_code == 0 assert repo.git.tag(l=f"*{metadata_suffix}") @@ -500,7 +512,7 @@ def test_version_build_metadata_triggers_new_version(repo: Repo, cli_runner: Cli def test_version_prints_current_version_if_no_new_version( repo_with_git_flow_angular_commits: Repo, cli_runner: CliRunner ): - result = cli_runner.invoke(main, [version.name or "version", "--no-push"]) + result = cli_runner.invoke(main, [version_subcmd or "version", "--no-push"]) assert result.exit_code == 0 assert "no release will be made" in result.stderr.lower() assert result.stdout == "1.2.0-alpha.2\n" @@ -527,7 +539,7 @@ def test_version_runs_build_command( ): # ACT: run & force a new version that will trigger the build command result = cli_runner.invoke( - main, [version.name or "version", "--patch", "--no-push"] + main, [version_subcmd or "version", "--patch", "--no-push"] ) assert result.exit_code == 0 @@ -543,7 +555,7 @@ def test_version_skips_build_command_with_skip_build( "subprocess.run", return_value=CompletedProcess(args=(), returncode=0) ) as patched_subprocess_run: result = cli_runner.invoke( - main, [version.name, "--patch", "--no-push", "--skip-build"] + main, [version_subcmd, "--patch", "--no-push", "--skip-build"] ) # force a new version assert result.exit_code == 0 @@ -555,7 +567,7 @@ def test_version_writes_github_actions_output( ): mock_output_file = tmp_path / "action.out" monkeypatch.setenv("GITHUB_OUTPUT", str(mock_output_file.resolve())) - result = cli_runner.invoke(main, [version.name, "--patch", "--no-push"]) + result = cli_runner.invoke(main, [version_subcmd, "--patch", "--no-push"]) assert result.exit_code == 0 action_outputs = actions_output_to_dict( @@ -570,7 +582,7 @@ def test_version_writes_github_actions_output( def test_version_exit_code_when_strict(repo_with_git_flow_angular_commits, cli_runner): - result = cli_runner.invoke(main, ["--strict", version.name, "--no-push"]) + result = cli_runner.invoke(main, ["--strict", version_subcmd, "--no-push"]) assert result.exit_code != 0 @@ -578,7 +590,7 @@ def test_version_exit_code_when_not_strict( repo_with_git_flow_angular_commits, cli_runner ): # Testing "no release will be made" - result = cli_runner.invoke(main, [version.name, "--no-push"]) + result = cli_runner.invoke(main, [version_subcmd, "--no-push"]) assert result.exit_code == 0 @@ -599,7 +611,7 @@ def test_custom_release_notes_template( # Act resp = cli_runner.invoke( - main, [version.name or "version", "--skip-build", "--vcs-release"] + main, [version_subcmd, "--skip-build", "--vcs-release"] ) release_history = get_release_history_from_context(runtime_context_with_no_tags) tag = runtime_context_with_no_tags.repo.tags[-1].name @@ -620,7 +632,7 @@ def test_custom_release_notes_template( assert mocked_git_push.call_count == 2 # 1 for commit, 1 for tag assert resp.exit_code == 0, ( "Unexpected failure in command " - f"'semantic-release {version.name} --skip-build --vcs-release': " + resp.stderr + f"'semantic-release {version_subcmd} --skip-build --vcs-release': " + resp.stderr ) assert post_mocker.call_count == 1 assert post_mocker.last_request is not None @@ -640,7 +652,7 @@ def test_version_tag_only_push( head_before = runtime_context_with_no_tags.repo.head.commit # Act - args = [version.name, "--tag", "--no-commit", "--skip-build", "--no-vcs-release"] + args = [version_subcmd, "--tag", "--no-commit", "--skip-build", "--no-vcs-release"] resp = cli_runner.invoke(main, args) tag_after = runtime_context_with_no_tags.repo.tags[-1].name @@ -682,7 +694,7 @@ def test_version_only_update_files_no_git_actions( tags_before = runtime_context_with_tags.repo.tags # Act - args = [version.name, "--minor", "--no-tag", "--no-commit", "--skip-build"] + args = [version_subcmd, "--minor", "--no-tag", "--no-commit", "--skip-build"] resp = cli_runner.invoke(main, args) tags_after = runtime_context_with_tags.repo.tags diff --git a/tests/scenario/test_template_render.py b/tests/scenario/test_template_render.py index 63aef9514..9e7960baf 100644 --- a/tests/scenario/test_template_render.py +++ b/tests/scenario/test_template_render.py @@ -10,9 +10,8 @@ from semantic_release.changelog.template import environment, recursive_render if TYPE_CHECKING: - from tests.conftest import ExProjectDir + from tests.fixtures.example_project import ExProjectDir -# FIX me NORMAL_TEMPLATE_SRC = """--- content: @@ -46,7 +45,7 @@ def _strip_trailing_j2(path: Path) -> Path: @pytest.fixture -def normal_template(example_project_template_dir): +def normal_template(example_project_template_dir: Path) -> Path: template = example_project_template_dir / "normal.yaml.j2" template.parent.mkdir(parents=True, exist_ok=True) template.write_text(NORMAL_TEMPLATE_SRC) @@ -54,7 +53,7 @@ def normal_template(example_project_template_dir): @pytest.fixture -def long_directory_path(example_project_template_dir): +def long_directory_path(example_project_template_dir: Path) -> Path: # NOTE: fixture enables using Path rather than # constant string, so no issue with / vs \ on Windows folder = example_project_template_dir / "long" / "dir" / "path" @@ -62,7 +61,7 @@ def long_directory_path(example_project_template_dir): @pytest.fixture -def deeply_nested_file(long_directory_path): +def deeply_nested_file(long_directory_path: Path) -> Path: file = long_directory_path / "buried.txt" file.parent.mkdir(parents=True, exist_ok=True) file.write_text(PLAINTEXT_FILE_CONTENT) @@ -70,7 +69,7 @@ def deeply_nested_file(long_directory_path): @pytest.fixture -def hidden_file(example_project_template_dir): +def hidden_file(example_project_template_dir: Path) -> Path: file = example_project_template_dir / ".hidden" file.parent.mkdir(parents=True, exist_ok=True) file.write_text("I shouldn't be present") @@ -84,7 +83,7 @@ def directory_path_with_hidden_subfolder(example_project_template_dir: Path) -> @pytest.fixture -def excluded_file(directory_path_with_hidden_subfolder): +def excluded_file(directory_path_with_hidden_subfolder: Path) -> Path: file = directory_path_with_hidden_subfolder / "excluded.txt" file.parent.mkdir(parents=True, exist_ok=True) file.write_text("I shouldn't be present") @@ -155,7 +154,10 @@ def dotfolder_template(init_example_project: None, dotfolder_template_dir: Path) def test_recursive_render_with_top_level_dotfolder( - example_project_dir, dotfolder_template, dotfolder_template_dir + init_example_project: None, + example_project_dir: ExProjectDir, + dotfolder_template: Path, + dotfolder_template_dir: Path, ): preexisting_paths = set(example_project_dir.rglob("**/*")) env = environment(template_dir=dotfolder_template_dir.resolve()) diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index 8956c259c..72447c6d5 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -27,6 +27,10 @@ from pathlib import Path from typing import Any + from git import Repo + + from tests.fixtures.example_project import ExProjectDir + @pytest.mark.parametrize( "remote_config, expected_token", @@ -115,7 +119,7 @@ def test_invalid_commit_parser_value(commit_parser: str): assert "commit_parser" in str(excinfo.value) -def test_default_toml_config_valid(example_project_dir): +def test_default_toml_config_valid(example_project_dir: ExProjectDir): default_config_file = example_project_dir / "default.toml" default_config_file.write_text( @@ -142,9 +146,9 @@ def test_default_toml_config_valid(example_project_dir): ) def test_commit_author_configurable( example_pyproject_toml: Path, - repo_with_no_tags_angular_commits, - mock_env, - expected_author, + repo_with_no_tags_angular_commits: Repo, + mock_env: dict[str, str], + expected_author: str, ): content = tomlkit.loads(example_pyproject_toml.read_text(encoding="utf-8")).unwrap() From f802446bd0693c4c9f6bdfdceae8b89c447827d2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 7 Jan 2024 15:01:51 -0500 Subject: [PATCH 135/167] fix(changelog-generation): fix incorrect release timezone determination --- semantic_release/changelog/release_history.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/semantic_release/changelog/release_history.py b/semantic_release/changelog/release_history.py index 483d2117e..813d25b6f 100644 --- a/semantic_release/changelog/release_history.py +++ b/semantic_release/changelog/release_history.py @@ -81,7 +81,7 @@ def from_git_history( if isinstance(tag.object, TagObject): tagger = tag.object.tagger committer = tag.object.tagger.committer() - _tz = timezone(timedelta(seconds=tag.object.tagger_tz_offset)) + _tz = timezone(timedelta(seconds=-1 * tag.object.tagger_tz_offset)) tagged_date = datetime.fromtimestamp( tag.object.tagged_date, tz=_tz ) @@ -89,7 +89,7 @@ def from_git_history( # For some reason, sometimes tag.object is a Commit tagger = tag.object.author committer = tag.object.author - _tz = timezone(timedelta(seconds=tag.object.author_tz_offset)) + _tz = timezone(timedelta(seconds=-1 * tag.object.author_tz_offset)) tagged_date = datetime.fromtimestamp( tag.object.committed_date, tz=_tz ) From 1573b6b7def84fabeaebdeb590d65068e6ed9a5d Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 21:45:35 -0500 Subject: [PATCH 136/167] test(changelog): increase changelog rigor to all commit types --- tests/command_line/test_changelog.py | 62 ++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index abece8368..6b892af60 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -19,6 +19,33 @@ EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, ) +from tests.fixtures.repos import ( + repo_with_no_tags_angular_commits, + repo_with_no_tags_emoji_commits, + repo_with_no_tags_scipy_commits, + repo_with_no_tags_tag_commits, + repo_with_single_branch_angular_commits, + repo_with_single_branch_emoji_commits, + repo_with_single_branch_scipy_commits, + repo_with_single_branch_tag_commits, + repo_with_single_branch_and_prereleases_angular_commits, + repo_with_single_branch_and_prereleases_emoji_commits, + repo_with_single_branch_and_prereleases_scipy_commits, + repo_with_single_branch_and_prereleases_tag_commits, + repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_w_github_flow_w_feature_release_channel_emoji_commits, + repo_w_github_flow_w_feature_release_channel_scipy_commits, + repo_w_github_flow_w_feature_release_channel_tag_commits, + repo_with_git_flow_angular_commits, + repo_with_git_flow_emoji_commits, + repo_with_git_flow_scipy_commits, + repo_with_git_flow_tag_commits, + repo_with_git_flow_and_release_channels_angular_commits, + repo_with_git_flow_and_release_channels_emoji_commits, + repo_with_git_flow_and_release_channels_scipy_commits, + repo_with_git_flow_and_release_channels_tag_commits, + repo_with_git_flow_and_release_channels_angular_commits_using_tag_format, +) from tests.util import flatten_dircmp, get_release_history_from_context, remove_dir_tree if TYPE_CHECKING: @@ -107,13 +134,34 @@ def test_changelog_noop_is_noop( @pytest.mark.parametrize( "repo", [ - lazy_fixture("repo_with_no_tags_angular_commits"), - lazy_fixture("repo_with_single_branch_angular_commits"), - lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), - lazy_fixture("repo_with_git_flow_angular_commits"), - lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), - lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits_using_tag_format"), + lazy_fixture(repo_fixture) + for repo_fixture in [ + repo_with_no_tags_angular_commits.__name__, + repo_with_no_tags_emoji_commits.__name__, + repo_with_no_tags_scipy_commits.__name__, + repo_with_no_tags_tag_commits.__name__, + repo_with_single_branch_angular_commits.__name__, + repo_with_single_branch_emoji_commits.__name__, + repo_with_single_branch_scipy_commits.__name__, + repo_with_single_branch_tag_commits.__name__, + repo_with_single_branch_and_prereleases_angular_commits.__name__, + repo_with_single_branch_and_prereleases_emoji_commits.__name__, + repo_with_single_branch_and_prereleases_scipy_commits.__name__, + repo_with_single_branch_and_prereleases_tag_commits.__name__, + repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, + repo_w_github_flow_w_feature_release_channel_emoji_commits.__name__, + repo_w_github_flow_w_feature_release_channel_scipy_commits.__name__, + repo_w_github_flow_w_feature_release_channel_tag_commits.__name__, + repo_with_git_flow_angular_commits.__name__, + repo_with_git_flow_emoji_commits.__name__, + repo_with_git_flow_scipy_commits.__name__, + repo_with_git_flow_tag_commits.__name__, + repo_with_git_flow_and_release_channels_angular_commits.__name__, + repo_with_git_flow_and_release_channels_emoji_commits.__name__, + repo_with_git_flow_and_release_channels_scipy_commits.__name__, + repo_with_git_flow_and_release_channels_tag_commits.__name__, + repo_with_git_flow_and_release_channels_angular_commits_using_tag_format.__name__, + ] ], ) def test_changelog_content_regenerated( From a841f3baa909768947ffac742a33ca18a40cc01b Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 21:46:24 -0500 Subject: [PATCH 137/167] style(test-changelog): change to direct fixture reference & improve clarity --- tests/command_line/test_changelog.py | 110 ++++++++++++++++----------- tests/const.py | 2 + 2 files changed, 66 insertions(+), 46 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 6b892af60..35acd3cd2 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -18,33 +18,34 @@ EXAMPLE_RELEASE_NOTES_TEMPLATE, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, + SUCCESS_EXIT_CODE, ) from tests.fixtures.repos import ( - repo_with_no_tags_angular_commits, - repo_with_no_tags_emoji_commits, - repo_with_no_tags_scipy_commits, - repo_with_no_tags_tag_commits, - repo_with_single_branch_angular_commits, - repo_with_single_branch_emoji_commits, - repo_with_single_branch_scipy_commits, - repo_with_single_branch_tag_commits, - repo_with_single_branch_and_prereleases_angular_commits, - repo_with_single_branch_and_prereleases_emoji_commits, - repo_with_single_branch_and_prereleases_scipy_commits, - repo_with_single_branch_and_prereleases_tag_commits, repo_w_github_flow_w_feature_release_channel_angular_commits, repo_w_github_flow_w_feature_release_channel_emoji_commits, repo_w_github_flow_w_feature_release_channel_scipy_commits, repo_w_github_flow_w_feature_release_channel_tag_commits, - repo_with_git_flow_angular_commits, - repo_with_git_flow_emoji_commits, - repo_with_git_flow_scipy_commits, - repo_with_git_flow_tag_commits, repo_with_git_flow_and_release_channels_angular_commits, + repo_with_git_flow_and_release_channels_angular_commits_using_tag_format, repo_with_git_flow_and_release_channels_emoji_commits, repo_with_git_flow_and_release_channels_scipy_commits, repo_with_git_flow_and_release_channels_tag_commits, - repo_with_git_flow_and_release_channels_angular_commits_using_tag_format, + repo_with_git_flow_angular_commits, + repo_with_git_flow_emoji_commits, + repo_with_git_flow_scipy_commits, + repo_with_git_flow_tag_commits, + repo_with_no_tags_angular_commits, + repo_with_no_tags_emoji_commits, + repo_with_no_tags_scipy_commits, + repo_with_no_tags_tag_commits, + repo_with_single_branch_and_prereleases_angular_commits, + repo_with_single_branch_and_prereleases_emoji_commits, + repo_with_single_branch_and_prereleases_scipy_commits, + repo_with_single_branch_and_prereleases_tag_commits, + repo_with_single_branch_angular_commits, + repo_with_single_branch_emoji_commits, + repo_with_single_branch_scipy_commits, + repo_with_single_branch_tag_commits, ) from tests.util import flatten_dircmp, get_release_history_from_context, remove_dir_tree @@ -59,27 +60,31 @@ from tests.fixtures.example_project import ExProjectDir, UseReleaseNotesTemplateFn -changelog_subcmd = changelog.name or changelog.__name__ +changelog_subcmd = changelog.name or changelog.__name__ # type: ignore @pytest.mark.parametrize( "repo,tag", [ - (lazy_fixture("repo_with_no_tags_angular_commits"), None), - (lazy_fixture("repo_with_single_branch_angular_commits"), "v0.1.1"), + (lazy_fixture(repo_with_no_tags_angular_commits.__name__), None), + (lazy_fixture(repo_with_single_branch_angular_commits.__name__), "v0.1.1"), ( - lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), + lazy_fixture( + repo_with_single_branch_and_prereleases_angular_commits.__name__ + ), "v0.2.0", ), ( lazy_fixture( - "repo_w_github_flow_w_feature_release_channel_angular_commits" + repo_w_github_flow_w_feature_release_channel_angular_commits.__name__ ), "v0.2.0", ), - (lazy_fixture("repo_with_git_flow_angular_commits"), "v1.0.0"), + (lazy_fixture(repo_with_git_flow_angular_commits.__name__), "v1.0.0"), ( - lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), + lazy_fixture( + repo_with_git_flow_and_release_channels_angular_commits.__name__ + ), "v1.1.0-alpha.3", ), ], @@ -115,11 +120,9 @@ def test_changelog_noop_is_noop( "semantic_release.hvcs.github.build_requests_session", return_value=session, ), requests_mock.Mocker(session=session) as mocker: - result = cli_runner.invoke( - main, ["--noop", changelog_subcmd, *args] - ) + result = cli_runner.invoke(main, ["--noop", changelog_subcmd, *args]) - assert result.exit_code == 0 + assert SUCCESS_EXIT_CODE == result.exit_code # noqa: SIM300 dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) @@ -175,7 +178,7 @@ def test_changelog_content_regenerated( os.remove(str(example_changelog_md.resolve())) result = cli_runner.invoke(main, [changelog_subcmd]) - assert result.exit_code == 0 + assert SUCCESS_EXIT_CODE == result.exit_code # noqa: SIM300 # Check that the changelog file was re-created assert example_changelog_md.exists() @@ -187,7 +190,9 @@ def test_changelog_content_regenerated( # Just need to test that it works for "a" project, not all -@pytest.mark.usefixtures("repo_with_single_branch_and_prereleases_angular_commits") +@pytest.mark.usefixtures( + repo_with_single_branch_and_prereleases_angular_commits.__name__ +) @pytest.mark.parametrize( "args", [("--post-to-release-tag", "v1.99.91910000000000000000000000000")] ) @@ -197,16 +202,20 @@ def test_changelog_release_tag_not_in_history( example_project_dir: ExProjectDir, cli_runner: CliRunner, ): + expected_err_code = 2 tempdir = tmp_path_factory.mktemp("test_changelog") remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) result = cli_runner.invoke(main, [changelog_subcmd, *args]) - assert result.exit_code == 2 + + assert expected_err_code == result.exit_code assert "not in release history" in result.stderr.lower() -@pytest.mark.usefixtures("repo_with_single_branch_and_prereleases_angular_commits") +@pytest.mark.usefixtures( + repo_with_single_branch_and_prereleases_angular_commits.__name__ +) @pytest.mark.parametrize("args", [("--post-to-release-tag", "v0.1.0")]) def test_changelog_post_to_release( args: list[str], @@ -232,6 +241,14 @@ def test_changelog_post_to_release( session.mount("http://", mock_adapter) session.mount("https://", mock_adapter) + expected_request_url = ( + "https://{api_url}/repos/{owner}/{repo_name}/releases".format( + api_url=Github.DEFAULT_API_DOMAIN, + owner=EXAMPLE_REPO_OWNER, + repo_name=EXAMPLE_REPO_NAME, + ) + ) + # Patch out env vars that affect changelog URLs but only get set in e.g. # Github actions with mock.patch( @@ -242,17 +259,12 @@ def test_changelog_post_to_release( m.delenv("CI_PROJECT_NAMESPACE", raising=False) result = cli_runner.invoke(main, [changelog_subcmd, *args]) - assert result.exit_code == 0 + assert SUCCESS_EXIT_CODE == result.exit_code # noqa: SIM300 assert mocker.called - assert mock_adapter.called and mock_adapter.last_request is not None - assert mock_adapter.last_request.url == ( - "https://{api_url}/repos/{owner}/{repo_name}/releases".format( - api_url=Github.DEFAULT_API_DOMAIN, - owner=EXAMPLE_REPO_OWNER, - repo_name=EXAMPLE_REPO_NAME, - ) - ) + assert mock_adapter.called + assert mock_adapter.last_request is not None + assert expected_request_url == mock_adapter.last_request.url def test_custom_release_notes_template( @@ -268,6 +280,8 @@ def test_custom_release_notes_template( runtime_context_with_tags = retrieve_runtime_context( repo_with_single_branch_and_prereleases_angular_commits ) + expected_call_count = 1 + # Arrange release_history = get_release_history_from_context(runtime_context_with_tags) tag = runtime_context_with_tags.repo.tags[-1].name @@ -280,15 +294,19 @@ def test_custom_release_notes_template( # Act resp = cli_runner.invoke(main, [changelog_subcmd, "--post-to-release-tag", tag]) - expected_release_notes = runtime_context_with_tags.template_environment.from_string( - EXAMPLE_RELEASE_NOTES_TEMPLATE - ).render(version=version, release=release) + '\n' + expected_release_notes = ( + runtime_context_with_tags.template_environment.from_string( + EXAMPLE_RELEASE_NOTES_TEMPLATE + ).render(version=version, release=release) + + "\n" + ) # Assert - assert resp.exit_code == 0, ( + assert SUCCESS_EXIT_CODE == resp.exit_code, ( # noqa: SIM300 "Unexpected failure in command " f"'semantic-release {changelog_subcmd} --post-to-release-tag {tag}': " + resp.stderr ) - assert post_mocker.call_count == 1 and post_mocker.last_request is not None + assert expected_call_count == post_mocker.call_count + assert post_mocker.last_request is not None assert expected_release_notes == post_mocker.last_request.json()["body"] diff --git a/tests/const.py b/tests/const.py index 7108f892a..9801b13c7 100644 --- a/tests/const.py +++ b/tests/const.py @@ -8,6 +8,8 @@ EXAMPLE_REPO_NAME = "example_repo" EXAMPLE_HVCS_DOMAIN = "example.com" +SUCCESS_EXIT_CODE = 0 + TODAY_DATE_STR = datetime.now().strftime("%Y-%m-%d") """Date formatted as how it would appear in the changelog (Must match local timezone)""" From 747fe1da682764da1a7c9199f43c02fdf998739d Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 22:20:03 -0500 Subject: [PATCH 138/167] style: resolve linter & formatting across codebase --- semantic_release/changelog/release_history.py | 8 +- semantic_release/cli/common.py | 8 +- semantic_release/commit_parser/util.py | 2 +- tests/command_line/test_version.py | 33 +++---- tests/fixtures/git_repo.py | 40 ++++----- tests/scenario/test_release_history.py | 53 +++++++---- tests/scenario/test_template_render.py | 13 +-- .../changelog/test_default_changelog.py | 88 +++++++++++-------- .../changelog/test_release_notes.py | 25 +++--- 9 files changed, 159 insertions(+), 111 deletions(-) diff --git a/semantic_release/changelog/release_history.py b/semantic_release/changelog/release_history.py index 813d25b6f..f34fc3e51 100644 --- a/semantic_release/changelog/release_history.py +++ b/semantic_release/changelog/release_history.py @@ -81,7 +81,9 @@ def from_git_history( if isinstance(tag.object, TagObject): tagger = tag.object.tagger committer = tag.object.tagger.committer() - _tz = timezone(timedelta(seconds=-1 * tag.object.tagger_tz_offset)) + _tz = timezone( + timedelta(seconds=-1 * tag.object.tagger_tz_offset) + ) tagged_date = datetime.fromtimestamp( tag.object.tagged_date, tz=_tz ) @@ -89,7 +91,9 @@ def from_git_history( # For some reason, sometimes tag.object is a Commit tagger = tag.object.author committer = tag.object.author - _tz = timezone(timedelta(seconds=-1 * tag.object.author_tz_offset)) + _tz = timezone( + timedelta(seconds=-1 * tag.object.author_tz_offset) + ) tagged_date = datetime.fromtimestamp( tag.object.committed_date, tz=_tz ) diff --git a/semantic_release/cli/common.py b/semantic_release/cli/common.py index c0ede5ac2..d6dbc98ee 100644 --- a/semantic_release/cli/common.py +++ b/semantic_release/cli/common.py @@ -43,6 +43,8 @@ def render_release_notes( version: Version, release: Release, ) -> str: - return template_environment.from_string(release_notes_template).render( - version=version, release=release - ).rstrip() + return ( + template_environment.from_string(release_notes_template) + .render(version=version, release=release) + .rstrip() + ) diff --git a/semantic_release/commit_parser/util.py b/semantic_release/commit_parser/util.py index 8bffae7fe..6ad7660aa 100644 --- a/semantic_release/commit_parser/util.py +++ b/semantic_release/commit_parser/util.py @@ -6,7 +6,7 @@ def parse_paragraphs(text: str) -> list[str]: - """ + r""" This will take a text block and return a list containing each paragraph with single line breaks collapsed into spaces. diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 2b86dc354..8e2da4eec 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -439,11 +439,13 @@ def test_version_no_push_force_level( differing_files = sorted(flatten_dircmp(dcmp)) # Changelog already reflects changes this should introduce - assert differing_files == sorted([ - "CHANGELOG.md", - "pyproject.toml", - f"src/{EXAMPLE_PROJECT_NAME}/_version.py", - ]) + assert differing_files == sorted( + [ + "CHANGELOG.md", + "pyproject.toml", + f"src/{EXAMPLE_PROJECT_NAME}/_version.py", + ] + ) # Compare pyproject.toml new_pyproject_toml = tomlkit.loads( @@ -610,9 +612,7 @@ def test_custom_release_notes_template( ) # Act - resp = cli_runner.invoke( - main, [version_subcmd, "--skip-build", "--vcs-release"] - ) + resp = cli_runner.invoke(main, [version_subcmd, "--skip-build", "--vcs-release"]) release_history = get_release_history_from_context(runtime_context_with_no_tags) tag = runtime_context_with_no_tags.repo.tags[-1].name @@ -632,7 +632,8 @@ def test_custom_release_notes_template( assert mocked_git_push.call_count == 2 # 1 for commit, 1 for tag assert resp.exit_code == 0, ( "Unexpected failure in command " - f"'semantic-release {version_subcmd} --skip-build --vcs-release': " + resp.stderr + f"'semantic-release {version_subcmd} --skip-build --vcs-release': " + + resp.stderr ) assert post_mocker.call_count == 1 assert post_mocker.last_request is not None @@ -715,12 +716,14 @@ def test_version_only_update_files_no_git_actions( differing_files = sorted(flatten_dircmp(dcmp)) # Files that should receive version change - expected_changed_files = sorted([ - # CHANGELOG.md is not included as no modification to Git History - # (no commit or tag) has been made - "pyproject.toml", - f"src/{EXAMPLE_PROJECT_NAME}/_version.py", - ]) + expected_changed_files = sorted( + [ + # CHANGELOG.md is not included as no modification to Git History + # (no commit or tag) has been made + "pyproject.toml", + f"src/{EXAMPLE_PROJECT_NAME}/_version.py", + ] + ) assert expected_changed_files == differing_files # Compare pyproject.toml diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 4c5eba4fc..d10b870d8 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -326,21 +326,21 @@ def _build_configured_base_repo( @pytest.fixture(scope="session") def simulate_default_changelog_creation() -> SimulateDefaultChangelogCreationFn: def build_version_entry(version: VersionStr, version_def: RepoVersionDef) -> str: - version_entry = [] - if version == "Unreleased": - version_entry.append(f"## {version}\n") - else: - version_entry.append( - # TODO: artificial newline in front due to template when no Unreleased changes exist - f"\n## v{version} ({TODAY_DATE_STR})\n" - ) + version_entry = [] + if version == "Unreleased": + version_entry.append(f"## {version}\n") + else: + version_entry.append( + # TODO: artificial newline in front due to template when no Unreleased changes exist + f"\n## v{version} ({TODAY_DATE_STR})\n" + ) - for section_def in version_def["changelog_sections"]: - version_entry.append(f"### {section_def['section']}\n") - for i in section_def["i_commits"]: - version_entry.append(f"* {version_def['commits'][i]}\n") + for section_def in version_def["changelog_sections"]: + version_entry.append(f"### {section_def['section']}\n") + for i in section_def["i_commits"]: + version_entry.append(f"* {version_def['commits'][i]}\n") - return str.join("\n", version_entry) + return str.join("\n", version_entry) def _mimic_semantic_release_default_changelog( repo_definition: RepoDefinition, @@ -351,16 +351,12 @@ def _mimic_semantic_release_default_changelog( for version, version_def in repo_definition.items(): # prepend entries to force reverse ordering - version_entries.insert( - 0, build_version_entry(version, version_def) - ) + version_entries.insert(0, build_version_entry(version, version_def)) - changelog_content = str.join("\n" * 3, [ - header, - str.join("\n", [ - entry for entry in version_entries - ]) - ]).rstrip() + '\n' + changelog_content = ( + str.join("\n" * 3, [header, str.join("\n", list(version_entries))]).rstrip() + + "\n" + ) if dest_file is not None: dest_file.write_text(changelog_content) diff --git a/tests/scenario/test_release_history.py b/tests/scenario/test_release_history.py index ce73e8ffb..0c51ced8f 100644 --- a/tests/scenario/test_release_history.py +++ b/tests/scenario/test_release_history.py @@ -235,12 +235,19 @@ def test_release_history( for k in expected_release_history.released: expected = expected_release_history.released[k] actual = released[k]["elements"] - actual_released_messages = str.join("\n\n", sorted([ - str(res.commit.message) for results in actual.values() for res in results - ])) - expected_released_messages = str.join("\n\n", sorted([ - msg for bucket in expected.values() for msg in bucket - ])) + actual_released_messages = str.join( + "\n\n", + sorted( + [ + str(res.commit.message) + for results in actual.values() + for res in results + ] + ), + ) + expected_released_messages = str.join( + "\n\n", sorted([msg for bucket in expected.values() for msg in bucket]) + ) assert expected_released_messages == actual_released_messages for commit_message in ANGULAR_COMMITS_MINOR: @@ -252,18 +259,30 @@ def test_release_history( repo, translator, default_angular_parser ) - actual_unreleased_messages = str.join("\n\n", sorted([ - str(res.commit.message) for results in new_unreleased.values() for res in results - ])) + actual_unreleased_messages = str.join( + "\n\n", + sorted( + [ + str(res.commit.message) + for results in new_unreleased.values() + for res in results + ] + ), + ) - expected_unreleased_messages = str.join("\n\n", sorted([ - msg - for bucket in [ - ANGULAR_COMMITS_MINOR[::-1], - *expected_release_history.unreleased.values(), - ] - for msg in bucket - ])) + expected_unreleased_messages = str.join( + "\n\n", + sorted( + [ + msg + for bucket in [ + ANGULAR_COMMITS_MINOR[::-1], + *expected_release_history.unreleased.values(), + ] + for msg in bucket + ] + ), + ) assert expected_unreleased_messages == actual_unreleased_messages assert ( diff --git a/tests/scenario/test_template_render.py b/tests/scenario/test_template_render.py index 9e7960baf..b3d1f1598 100644 --- a/tests/scenario/test_template_render.py +++ b/tests/scenario/test_template_render.py @@ -2,7 +2,6 @@ import itertools import os -from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -10,6 +9,8 @@ from semantic_release.changelog.template import environment, recursive_render if TYPE_CHECKING: + from pathlib import Path + from tests.fixtures.example_project import ExProjectDir @@ -56,8 +57,7 @@ def normal_template(example_project_template_dir: Path) -> Path: def long_directory_path(example_project_template_dir: Path) -> Path: # NOTE: fixture enables using Path rather than # constant string, so no issue with / vs \ on Windows - folder = example_project_template_dir / "long" / "dir" / "path" - return folder + return example_project_template_dir / "long" / "dir" / "path" @pytest.fixture @@ -78,8 +78,7 @@ def hidden_file(example_project_template_dir: Path) -> Path: @pytest.fixture def directory_path_with_hidden_subfolder(example_project_template_dir: Path) -> Path: - folder = example_project_template_dir / "path" / ".subfolder" / "hidden" - return folder + return example_project_template_dir / "path" / ".subfolder" / "hidden" @pytest.fixture @@ -146,7 +145,9 @@ def dotfolder_template_dir(example_project_dir: ExProjectDir) -> Path: @pytest.fixture -def dotfolder_template(init_example_project: None, dotfolder_template_dir: Path) -> Path: +def dotfolder_template( + init_example_project: None, dotfolder_template_dir: Path +) -> Path: tmpl = dotfolder_template_dir / "template.txt" tmpl.parent.mkdir(parents=True, exist_ok=True) tmpl.write_text("I am a template") diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index cc138b0de..0f8b3c097 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -86,9 +86,9 @@ def artificial_release_history(commit_author: Actor): elements={ "feature": [feat_commit_parsed], "fix": [fix_commit_parsed], - } + }, ) - } + }, ) @@ -104,23 +104,32 @@ def test_default_changelog_template( rh = artificial_release_history rh.unreleased = {} # Wipe out unreleased - feat_commit_obj = artificial_release_history.released[version]["elements"]["feature"][0] - feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url(feat_commit_obj.commit.hexsha) - feat_description = str.join('\n', feat_commit_obj.descriptions) + feat_commit_obj = artificial_release_history.released[version]["elements"][ + "feature" + ][0] + feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url( + feat_commit_obj.commit.hexsha + ) + feat_description = str.join("\n", feat_commit_obj.descriptions) fix_commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] - fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url(fix_commit_obj.commit.hexsha) - fix_description = str.join('\n', fix_commit_obj.descriptions) - - expected_changelog = str.join("\n", [ - "# CHANGELOG", - f"## v{version_str} ({TODAY_DATE_STR})", - "### Feature", - f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", - "### Fix", - f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", - "", - ]) + fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url( + fix_commit_obj.commit.hexsha + ) + fix_description = str.join("\n", fix_commit_obj.descriptions) + + expected_changelog = str.join( + "\n", + [ + "# CHANGELOG", + f"## v{version_str} ({TODAY_DATE_STR})", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "### Fix", + f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", + "", + ], + ) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) context = make_changelog_context( hvcs_client=hvcs_client(remote_url=example_git_https_url), @@ -141,26 +150,35 @@ def test_default_changelog_template_w_unreleased_changes( version_str = "1.0.0" version = Version.parse(version_str) - feat_commit_obj = artificial_release_history.released[version]["elements"]["feature"][0] - feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url(feat_commit_obj.commit.hexsha) - feat_description = str.join('\n', feat_commit_obj.descriptions) + feat_commit_obj = artificial_release_history.released[version]["elements"][ + "feature" + ][0] + feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url( + feat_commit_obj.commit.hexsha + ) + feat_description = str.join("\n", feat_commit_obj.descriptions) fix_commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] - fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url(fix_commit_obj.commit.hexsha) - fix_description = str.join('\n', fix_commit_obj.descriptions) - - expected_changelog = str.join("\n", [ - "# CHANGELOG", - "## Unreleased", - "### Feature", - f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", - f"## v{version_str} ({TODAY_DATE_STR})", - "### Feature", - f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", - "### Fix", - f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", - "", - ]) + fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url( + fix_commit_obj.commit.hexsha + ) + fix_description = str.join("\n", fix_commit_obj.descriptions) + + expected_changelog = str.join( + "\n", + [ + "# CHANGELOG", + "## Unreleased", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + f"## v{version_str} ({TODAY_DATE_STR})", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "### Fix", + f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", + "", + ], + ) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) context = make_changelog_context( hvcs_client=hvcs_client(remote_url=example_git_https_url), diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index cd2adf0e9..d4a2958f7 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -57,9 +57,9 @@ def artificial_release_history(commit_author: Actor): tagged_date=datetime.utcnow(), elements={ "fix": [fix_commit_parsed], - } + }, ) - } + }, ) @@ -87,14 +87,19 @@ def test_default_release_notes_template( version_str = "1.1.0-alpha.3" version = Version.parse(version_str) commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] - commit_url = hvcs_client(example_git_https_url).commit_hash_url(commit_obj.commit.hexsha) - commit_description = str.join('\n', commit_obj.descriptions) - expected_content = str.join("\n", [ - f"# v{version_str} ({TODAY_DATE_STR})", - "## Fix", - f"* {commit_description} ([`{commit_obj.commit.hexsha[:7]}`]({commit_url}))", - "", - ]) + commit_url = hvcs_client(example_git_https_url).commit_hash_url( + commit_obj.commit.hexsha + ) + commit_description = str.join("\n", commit_obj.descriptions) + expected_content = str.join( + "\n", + [ + f"# v{version_str} ({TODAY_DATE_STR})", + "## Fix", + f"* {commit_description} ([`{commit_obj.commit.hexsha[:7]}`]({commit_url}))", + "", + ], + ) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) context = make_changelog_context( hvcs_client=hvcs_client(remote_url=example_git_https_url), From b226906b3717d0307d242924a77cd847e7f9a50c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 21:47:16 -0500 Subject: [PATCH 139/167] test: add bitbucket to hvcs parameter list & bitbucket to configs --- tests/fixtures/example_project.py | 15 ++++++++++++++- tests/fixtures/git_repo.py | 3 +++ .../changelog/test_default_changelog.py | 6 +++--- .../changelog/test_release_notes.py | 4 ++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 8d2bfab4c..c1ea93191 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -14,7 +14,7 @@ ScipyCommitParser, TagCommitParser, ) -from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.hvcs import Bitbucket, Gitea, Github, Gitlab from tests.const import ( EXAMPLE_CHANGELOG_MD_CONTENT, @@ -376,3 +376,16 @@ def _use_gitea_hvcs(domain: str | None = None) -> type[HvcsBase]: return Gitea return _use_gitea_hvcs + + +@pytest.fixture(scope="session") +def use_bitbucket_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: + """Modify the configuration file to use BitBucket as the HVCS.""" + + def _use_bitbucket_hvcs(domain: str | None = None) -> type[HvcsBase]: + update_pyproject_toml("tool.semantic_release.remote.type", "bitbucket") + if domain is not None: + update_pyproject_toml("tool.semantic_release.remote.domain", domain) + return Bitbucket + + return _use_bitbucket_hvcs diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index d10b870d8..ae39a57f1 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -252,6 +252,7 @@ def build_configured_base_repo( use_github_hvcs: UseHvcsFn, use_gitlab_hvcs: UseHvcsFn, use_gitea_hvcs: UseHvcsFn, + use_bitbucket_hvcs: UseHvcsFn, use_angular_parser: UseParserFn, use_emoji_parser: UseParserFn, use_scipy_parser: UseParserFn, @@ -301,6 +302,8 @@ def _build_configured_base_repo( hvcs_class = use_gitlab_hvcs(hvcs_domain) elif hvcs_client_name == "gitea": hvcs_class = use_gitea_hvcs(hvcs_domain) + elif hvcs_client_name == "bitbucket": + hvcs_class = use_bitbucket_hvcs(hvcs_domain) else: raise ValueError(f"Unknown HVCS client name: {hvcs_client_name}") diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index 0f8b3c097..f5dc9fbc3 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -16,7 +16,7 @@ from semantic_release.changelog.template import environment from semantic_release.commit_parser import ParsedCommit from semantic_release.enums import LevelBump -from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.hvcs import Bitbucket, Gitea, Github, Gitlab from semantic_release.version.translator import Version from tests.const import TODAY_DATE_STR @@ -92,7 +92,7 @@ def artificial_release_history(commit_author: Actor): ) -@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) def test_default_changelog_template( default_changelog_template: str, hvcs_client: type[HvcsBase], @@ -140,7 +140,7 @@ def test_default_changelog_template( assert expected_changelog == actual_changelog -@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) def test_default_changelog_template_w_unreleased_changes( default_changelog_template: str, hvcs_client: type[HvcsBase], diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index d4a2958f7..285eea1b1 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -17,7 +17,7 @@ from semantic_release.changelog.template import environment from semantic_release.commit_parser import ParsedCommit from semantic_release.enums import LevelBump -from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.hvcs import Bitbucket, Gitea, Github, Gitlab from semantic_release.version import Version from tests.const import TODAY_DATE_STR @@ -72,7 +72,7 @@ def release_notes_template() -> str: return version_notes_template.read_text(encoding="utf-8") -@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) def test_default_release_notes_template( example_git_https_url: str, hvcs_client: type[HvcsBase], From 6242b61a4d87f6f4e27e3a65b2cd57e22394da48 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 22:29:47 -0500 Subject: [PATCH 140/167] test(fixtures): adjust scipy changelog expectations related to parse errors --- tests/fixtures/repos/git_flow/repo_w_3_release_channels.py | 2 +- tests/fixtures/repos/github_flow/repo_w_release_channels.py | 2 +- tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py | 2 +- tests/fixtures/repos/trunk_based_dev/repo_w_tags.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index a10b57de3..6077450bd 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -39,7 +39,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [ diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 5c84162c7..5e5e7c38e 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -39,7 +39,7 @@ def get_commits_for_github_flow_repo_w_feature_release_channel() -> GetRepoDefin "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [ diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 5ee445a1d..31dc41cef 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -52,7 +52,7 @@ def get_commits_for_trunk_only_repo_w_no_tags() -> GetRepoDefinitionFn: "scipy": [ {"section": "Feature", "i_commits": [2]}, {"section": "Fix", "i_commits": [1, 3]}, - {"section": "None", "i_commits": [0]}, + {"section": "Unknown", "i_commits": [0]}, ], "tag": [ {"section": "Feature", "i_commits": [2]}, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index 4b933bd41..0669e684d 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -39,7 +39,7 @@ def get_commits_for_trunk_only_repo_w_tags() -> GetRepoDefinitionFn: "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [ From adce5200910aa287c0925a6ca2c742fbd470209f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 22:39:48 -0500 Subject: [PATCH 141/167] test(fixtures): correct the ordering of commits in changelog expectations --- tests/fixtures/repos/git_flow/repo_w_2_release_channels.py | 2 +- tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py | 6 +++--- tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 4e03ebda4..7e47a6955 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -39,7 +39,7 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [ diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 31dc41cef..06ff258ae 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -45,18 +45,18 @@ def get_commits_for_trunk_only_repo_w_no_tags() -> GetRepoDefinitionFn: {"section": "Unknown", "i_commits": [0]}, ], "emoji": [ - {"section": ":bug:", "i_commits": [1, 3]}, + {"section": ":bug:", "i_commits": [3, 1]}, {"section": ":sparkles:", "i_commits": [2]}, {"section": "Other", "i_commits": [0]}, ], "scipy": [ {"section": "Feature", "i_commits": [2]}, - {"section": "Fix", "i_commits": [1, 3]}, + {"section": "Fix", "i_commits": [3, 1]}, {"section": "Unknown", "i_commits": [0]}, ], "tag": [ {"section": "Feature", "i_commits": [2]}, - {"section": "Fix", "i_commits": [1, 3]}, + {"section": "Fix", "i_commits": [3, 1]}, {"section": "Unknown", "i_commits": [0]}, ], }, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index 496f15940..b013dcee5 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -39,7 +39,7 @@ def get_commits_for_trunk_only_repo_w_prerelease_tags() -> GetRepoDefinitionFn: "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [ From 455ce4cfa88d801dd496187f757738e3cb34fd00 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Mon, 18 Mar 2024 15:01:31 +0000 Subject: [PATCH 142/167] 9.2.0 Automatically generated by python-semantic-release --- CHANGELOG.md | 113 ++++++++++++++++++++++++++++++++++- pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 112 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cb840697..1eb44f4ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,113 @@ +## v9.2.0 (2024-03-18) + +### Build + +* build(MANIFEST): fix sdist contents to include docs & tests ([`228347c`](https://github.com/python-semantic-release/python-semantic-release/commit/228347c8f64cc46f01b717ccdc2daf15384c7f2e)) + +* build(deps): add click-option-group for grouping exclusive flags ([`bd892b8`](https://github.com/python-semantic-release/python-semantic-release/commit/bd892b89c26df9fccc9335c84e2b3217e3e02a37)) + +### Chore + +* chore(stalebot): add config to manage aging issues & PRs ([`d69a69b`](https://github.com/python-semantic-release/python-semantic-release/commit/d69a69bb636c06d384ae29601c62971cf1d6e88a)) + +* chore(dependabot): adjust conf to relax bumping flexible requirement specs ([`a040aa4`](https://github.com/python-semantic-release/python-semantic-release/commit/a040aa43eb2218d76b3e56d280c7853633af4f45)) + +### Documentation + +* docs(configuration): clarify the `major_on_zero` configuration option ([`f7753cd`](https://github.com/python-semantic-release/python-semantic-release/commit/f7753cdabd07e276bc001478d605fca9a4b37ec4)) + +* docs(configuration): add description of `allow-zero-version` configuration option ([`4028f83`](https://github.com/python-semantic-release/python-semantic-release/commit/4028f8384a0181c8d58c81ae81cf0b241a02a710)) + +### Feature + +* feat(version-config): add option to disable 0.x.x versions ([`dedb3b7`](https://github.com/python-semantic-release/python-semantic-release/commit/dedb3b765c8530379af61d3046c3bb9c160d54e5)) + +* feat(version): add new version print flags to display the last released version and tag ([`814240c`](https://github.com/python-semantic-release/python-semantic-release/commit/814240c7355df95e9be9a6ed31d004b800584bc0)) + +### Fix + +* fix(changelog-generation): fix incorrect release timezone determination ([`f802446`](https://github.com/python-semantic-release/python-semantic-release/commit/f802446bd0693c4c9f6bdfdceae8b89c447827d2)) + +* fix(changelog): make sure default templates render ending in 1 newline ([`0b4a45e`](https://github.com/python-semantic-release/python-semantic-release/commit/0b4a45e3673d0408016dc8e7b0dce98007a763e3)) + +### Style + +* style: resolve linter & formatting across codebase ([`747fe1d`](https://github.com/python-semantic-release/python-semantic-release/commit/747fe1da682764da1a7c9199f43c02fdf998739d)) + +* style(test-changelog): change to direct fixture reference & improve clarity ([`a841f3b`](https://github.com/python-semantic-release/python-semantic-release/commit/a841f3baa909768947ffac742a33ca18a40cc01b)) + +* style(tests): add additional typing to test args ([`74c9dec`](https://github.com/python-semantic-release/python-semantic-release/commit/74c9dec2869c41700bbfe378f3f7833fa02a821c)) + +* style: apply ruff formatting to codebase ([`ced4caa`](https://github.com/python-semantic-release/python-semantic-release/commit/ced4caadf35b26eb573f6d6e19452d05c7e7dabb)) + +### Test + +* test(fixtures): correct the ordering of commits in changelog expectations ([`adce520`](https://github.com/python-semantic-release/python-semantic-release/commit/adce5200910aa287c0925a6ca2c742fbd470209f)) + +* test(fixtures): adjust scipy changelog expectations related to parse errors ([`6242b61`](https://github.com/python-semantic-release/python-semantic-release/commit/6242b61a4d87f6f4e27e3a65b2cd57e22394da48)) + +* test: add bitbucket to hvcs parameter list & bitbucket to configs ([`b226906`](https://github.com/python-semantic-release/python-semantic-release/commit/b226906b3717d0307d242924a77cd847e7f9a50c)) + +* test(changelog): increase changelog rigor to all commit types ([`1573b6b`](https://github.com/python-semantic-release/python-semantic-release/commit/1573b6b7def84fabeaebdeb590d65068e6ed9a5d)) + +* test(changelog): enforce common single newline after generated docs ([`77c3816`](https://github.com/python-semantic-release/python-semantic-release/commit/77c3816f79ad3c1ef9687304b4773e6485e4ee2f)) + +* test(fixtures): refactor for better chronological ordering for test success ([`929b861`](https://github.com/python-semantic-release/python-semantic-release/commit/929b861a5d827913efac325f9db6ae7b2f44ef77)) + +* test(unit-changelog): refactor template testing to be fast & simple ([`f8a718f`](https://github.com/python-semantic-release/python-semantic-release/commit/f8a718f7c00baa58521ffb21b231437652d1e102)) + +* test(unit-release-notes): refactor template testing + +Drop the test related release notes template as that is one more thing to +maintain and use the one provided to the user. ([`620d62a`](https://github.com/python-semantic-release/python-semantic-release/commit/620d62a60c47c3a8ffdc79cb74ee36ca330767e3)) + +* test(unit-changelog): drop context test & duplicate/incorrect template + +Drop the test related changelog template as that is one more thing to +maintain and does not actually match the embedded template provided to +the user. + +Secondly, drop the context unit test as it does not provide value that +the template ultimately will fail in other unit tests. ([`ebb5ca3`](https://github.com/python-semantic-release/python-semantic-release/commit/ebb5ca3ea208c15170f25376e749e61e3475d2ee)) + +* test(repo-commits): fix angular syntax for scopes in commits ([`a2b2b8f`](https://github.com/python-semantic-release/python-semantic-release/commit/a2b2b8f2ddeedb79e60cdb90de7a1d981509e814)) + +* test(fixtures): trigger changelog generation in repo fixtures ([`2a89f68`](https://github.com/python-semantic-release/python-semantic-release/commit/2a89f68bf14f540234cd218e3aab1c9e12707f21)) + +* test(fixtures): remove changelog generation prevention ([`7af8373`](https://github.com/python-semantic-release/python-semantic-release/commit/7af8373c3612e6aeec19c52c006187faa418b5be)) + +* test(cli-version): ensure CHANGELOG is included in changed files ([`8d119df`](https://github.com/python-semantic-release/python-semantic-release/commit/8d119df1b0ca04f4e06446c608a2322497b30aac)) + +* test: improve reliability & error readability of assertions ([`0b07786`](https://github.com/python-semantic-release/python-semantic-release/commit/0b0778617b8e0d73365f5b223270625227c3ca30)) + +* test(changelog): add assertion to check for changelog file exist before read ([`5556c3a`](https://github.com/python-semantic-release/python-semantic-release/commit/5556c3a881bfcc206df8adb21075b532a5551053)) + +* test(cli-changelog): refactor changelog re-gen test to show debuggable results ([`3f4ff05`](https://github.com/python-semantic-release/python-semantic-release/commit/3f4ff05c71d261b825d117ecd030d34e22009731)) + +* test(fixtures): add common fixture to write default changelog from repo definition ([`d2904b2`](https://github.com/python-semantic-release/python-semantic-release/commit/d2904b250b1285017316575d5effeb42b4440dfe)) + +* test(next-version): adapt scipy commits test for new allow_zero_version config option ([`d157ecf`](https://github.com/python-semantic-release/python-semantic-release/commit/d157ecf579594177de0b7cbc61b706f41db56fb9)) + +* test(next-version): adapt emoji commits test for new allow_zero_version config option ([`fc05b0e`](https://github.com/python-semantic-release/python-semantic-release/commit/fc05b0e334e5e8f9263626f3b68c32ee3a3b9bcc)) + +* test(next-version): adapt angular commits test for new allow_zero_version config option ([`5893831`](https://github.com/python-semantic-release/python-semantic-release/commit/58938315a3ea04db798dc40b7940f9d323a00aae)) + +* test(next-version): adapt tag commits test for new allow_zero_version config option ([`efe1672`](https://github.com/python-semantic-release/python-semantic-release/commit/efe1672788fd3659f53f60cc2fd72ca1bb9e40c0)) + +* test(next-version): add new test case to ensure minimum version determinations ([`7ed5fe5`](https://github.com/python-semantic-release/python-semantic-release/commit/7ed5fe504234acbfed72dd5fe4e76381475d4cce)) + +* test(next-version): refactor fixture references for maintainability ([`a2b43e2`](https://github.com/python-semantic-release/python-semantic-release/commit/a2b43e2759b334775edc9c65b0327b70808416d3)) + +* test(fixtures): add config modifier fixture ([`e3bb4d3`](https://github.com/python-semantic-release/python-semantic-release/commit/e3bb4d3dc8aa5e14b11396f4e9fe1901f20ed4a3)) + +* test(scenario): add variation of `allow_zero_version` flag ([`36142a6`](https://github.com/python-semantic-release/python-semantic-release/commit/36142a6d96446f4117220205fec8e6fe3d63ad42)) + +* test(unit): update unit test for incrementing version ([`791c69d`](https://github.com/python-semantic-release/python-semantic-release/commit/791c69d8d9968357284d884344e1710b9289688a)) + + ## v9.1.1 (2024-02-25) ### Fix @@ -4234,7 +4341,7 @@ file. ([`005dba0`](https://github.com/python-semantic-release/python-semantic-re * Merge branch 'tag-parser' ([`2519b42`](https://github.com/python-semantic-release/python-semantic-release/commit/2519b42c7381fe8217b34150bd1ad06b23c9a56d)) -## v2.1.4 (2015-08-23) +## v2.1.4 (2015-08-24) ### Fix @@ -4298,7 +4405,7 @@ Properties can only be used from instances. ([`7ecdeb2`](https://github.com/pyth * 2.1.1 ([`7cf3a7d`](https://github.com/python-semantic-release/python-semantic-release/commit/7cf3a7d9aa2adc5a3cebf9d1151b113388117312)) -## v2.1.0 (2015-08-19) +## v2.1.0 (2015-08-20) ### Chore @@ -4585,7 +4692,7 @@ related to #9 ([`a71b536`](https://github.com/python-semantic-release/python-sem * 0.3.2 ([`1d3ee00`](https://github.com/python-semantic-release/python-semantic-release/commit/1d3ee00c3601f06f900bc1694f3c7c32106a6e14)) -## v0.3.1 (2015-07-27) +## v0.3.1 (2015-07-28) ### Unknown diff --git a/pyproject.toml b/pyproject.toml index c8f892919..31dd59056 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.1.1" +version = "9.2.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index a51843a1b..14fca7015 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -23,7 +23,7 @@ tags_and_versions, ) -__version__ = "9.1.1" +__version__ = "9.2.0" def setup_hook(argv: list[str]) -> None: From 4b2467a1d7b14cd1afc3b355e69e81804fcc5b4d Mon Sep 17 00:00:00 2001 From: Robert Minsk Date: Mon, 11 Mar 2024 12:49:14 -0700 Subject: [PATCH 143/167] chore(pre-commit): cleanup vulture warnings --- pyproject.toml | 3 +++ tests/util.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 31dd59056..7ff75cac3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -345,6 +345,9 @@ section-order = [ ] sections = { "tests" = ["tests"] } +[tool.vulture] +ignore_names = ["change_to_ex_proj_dir", "init_example_project"] + [tool.semantic_release] logging_use_named_masks = true tag_format = "v{version}" diff --git a/tests/util.py b/tests/util.py index 871c8d4d1..fe1d24f80 100644 --- a/tests/util.py +++ b/tests/util.py @@ -56,7 +56,7 @@ def remove_dir_tree(directory: Path | str = ".", force: bool = False) -> None: read-only permissions """ - def on_read_only_error(func, path, exc_info): + def on_read_only_error(_func, path, _exc_info): os.chmod(path, stat.S_IWRITE) os.unlink(path) From 6e1f5cbd1c01f9a8c6cd8bafd706e5b5ec6ab007 Mon Sep 17 00:00:00 2001 From: Robert Minsk Date: Mon, 11 Mar 2024 12:51:37 -0700 Subject: [PATCH 144/167] chore(pre-commit): exclude generated file CHANGELOG.md from checks --- .pre-commit-config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f3882b75a..35ec180b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,6 +2,8 @@ default_language_version: python: python3 +exclude: "^CHANGELOG.md$" + repos: # Meta hooks - repo: meta From 7e938e69af7d45c6d77dcf0a3627e5663cb35bff Mon Sep 17 00:00:00 2001 From: Robert Minsk Date: Mon, 11 Mar 2024 12:53:50 -0700 Subject: [PATCH 145/167] chore(pre-commit): cleanup 'check blanket type ignore' warnings --- tests/unit/semantic_release/version/test_algorithm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/semantic_release/version/test_algorithm.py b/tests/unit/semantic_release/version/test_algorithm.py index 253b1c98f..9db7dd12f 100644 --- a/tests/unit/semantic_release/version/test_algorithm.py +++ b/tests/unit/semantic_release/version/test_algorithm.py @@ -29,7 +29,7 @@ def test_bfs_for_latest_version_in_history(): v1_commit = Commit(repo, binsha=b"0" * 20) class TagReferenceOverride(TagReference): - commit = v1_commit # type: ignore - mocking the commit property + commit = v1_commit # mocking the commit property v1_tag = TagReferenceOverride(repo, "refs/tags/v1.0.0", check_path=False) From 4cb4ca19dc3d649a54e7a1b1e9a44a196624eef0 Mon Sep 17 00:00:00 2001 From: Robert Minsk Date: Mon, 11 Mar 2024 14:14:26 -0700 Subject: [PATCH 146/167] chore(pre-commit): cleanup ruff warnings --- pyproject.toml | 6 +++++- semantic_release/hvcs/bitbucket.py | 17 ++++++++--------- tests/fixtures/git_repo.py | 4 ++-- .../semantic_release/commit_parser/test_util.py | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7ff75cac3..5ff6d4272 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -221,6 +221,9 @@ ignore = [ "INP001", # Errors should end with "Error" "N818", + # Fixtures that do not return a value need an underscore prefix. The rule + # does not handle generators. + "PT004", # flake8-pytest-style, values rowtype (list|tuple) "PT007", # pytest.raises needs a match - eventually want to enable @@ -285,7 +288,7 @@ line-ending = "lf" "semantic_release/hvcs/_base.py" = ["ARG002"] # from tests.fixtures import * is deliberate "tests/conftest.py" = ["F403"] -"tests/fixtures/__init__.py" = ["F403"] +"tests/fixtures/**/__init__.py" = ["F403"] "tests/*" = [ # unused arguments - likely fixtures to be moved to @@ -308,6 +311,7 @@ line-ending = "lf" "ANN", ] + [tool.ruff.lint.mccabe] max-complexity = 10 diff --git a/semantic_release/hvcs/bitbucket.py b/semantic_release/hvcs/bitbucket.py index 8258314e0..8b68fdcfc 100644 --- a/semantic_release/hvcs/bitbucket.py +++ b/semantic_release/hvcs/bitbucket.py @@ -37,7 +37,7 @@ class Bitbucket(HvcsBase): API_VERSION = "2.0" DEFAULT_DOMAIN = "bitbucket.org" DEFAULT_API_DOMAIN = "api.bitbucket.org" - DEFAULT_ENV_TOKEN_NAME = "BITBUCKET_TOKEN" + DEFAULT_ENV_TOKEN_NAME = "BITBUCKET_TOKEN" # noqa: S105 def __init__( self, @@ -93,14 +93,13 @@ def remote_url(self, use_token: bool = True) -> str: f"https://{user}:{self.token}@" f"{self.hvcs_domain}/{self.owner}/{self.repo_name}.git" ) - else: - # Note: Assume the token is a repository token which will only work on the - # repository it was created for. - # https://support.atlassian.com/bitbucket-cloud/docs/using-access-tokens - return ( - f"https://x-token-auth:{self.token}@" - f"{self.hvcs_domain}/{self.owner}/{self.repo_name}.git" - ) + # Note: Assume the token is a repository token which will only work on the + # repository it was created for. + # https://support.atlassian.com/bitbucket-cloud/docs/using-access-tokens + return ( + f"https://x-token-auth:{self.token}@" + f"{self.hvcs_domain}/{self.owner}/{self.repo_name}.git" + ) def commit_hash_url(self, commit_hash: str) -> str: return ( diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index ae39a57f1..c0b417d1e 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -247,7 +247,7 @@ def cached_example_git_project( @pytest.fixture(scope="session") -def build_configured_base_repo( +def build_configured_base_repo( # noqa: C901 cached_example_git_project: Path, use_github_hvcs: UseHvcsFn, use_gitlab_hvcs: UseHvcsFn, @@ -267,7 +267,7 @@ def build_configured_base_repo( derivative fixtures can call this fixture with individual parameters. """ - def _build_configured_base_repo( + def _build_configured_base_repo( # noqa: C901 dest_dir: Path | str, commit_type: str = "angular", hvcs_client_name: str = "github", diff --git a/tests/unit/semantic_release/commit_parser/test_util.py b/tests/unit/semantic_release/commit_parser/test_util.py index ea7037b0d..eed8117c8 100644 --- a/tests/unit/semantic_release/commit_parser/test_util.py +++ b/tests/unit/semantic_release/commit_parser/test_util.py @@ -16,7 +16,7 @@ ["Long explanation", "full of interesting details"], ), ( - # Windows (CRLF) + # Windows uses CRLF "Long\r\nexplanation\r\n\r\nfull of interesting\r\ndetails", ["Long explanation", "full of interesting details"], ), From ccdc9763b2f588bafe7a8c7d71342d4ebcb09f13 Mon Sep 17 00:00:00 2001 From: Robert Minsk Date: Mon, 11 Mar 2024 17:12:20 -0700 Subject: [PATCH 147/167] chore(deps-dev): bump ruff from 0.1.11 to 0.3.2 --- .pre-commit-config.yaml | 4 +++- pyproject.toml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 35ec180b9..27b4cdb68 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,12 +33,14 @@ repos: # Formatters that may modify source files automatically - repo: https://github.com/astral-sh/ruff-pre-commit + # Keep old ruff formatting rules until other merge requests are completed rev: v0.1.11 hooks: - id: ruff-format name: ruff (format) args: ["."] pass_filenames: false + - repo: https://github.com/adamchainz/blacken-docs rev: 1.16.0 hooks: @@ -53,7 +55,7 @@ repos: # Linters and validation - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.11 + rev: v0.3.2 hooks: - id: ruff name: ruff (lint) diff --git a/pyproject.toml b/pyproject.toml index 5ff6d4272..f57aeb7d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ test = [ "requests-mock>=1.10.0,<2", "types-pytest-lazy-fixture>=0.6.3.3", ] -dev = ["pre-commit", "tox", "ruff==0.1.11"] +dev = ["pre-commit", "tox", "ruff==0.3.2"] mypy = ["mypy", "types-requests"] [tool.pytest.ini_options] @@ -309,6 +309,8 @@ line-ending = "lf" "SLF001", # Annotations "ANN", + # Using format instead of f-string for readablity + "UP032" ] From d59f5931e6b7fac3604f9d5f13a1947688b285d9 Mon Sep 17 00:00:00 2001 From: Robert Minsk Date: Mon, 11 Mar 2024 18:26:15 -0700 Subject: [PATCH 148/167] chore(pre-commit): upgrade hooks for pyupgrade, mypy, vulture, bandit, and check-jsonschema --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 27b4cdb68..3a0f314c3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: additional_dependencies: ["black==23.10.1"] - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.1 hooks: - id: pyupgrade args: ["--py38-plus", "--keep-runtime-typing"] @@ -68,7 +68,7 @@ repos: pass_filenames: false - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.8.0" + rev: "v1.9.0" hooks: - id: mypy args: ["--config-file", "pyproject.toml"] @@ -93,7 +93,7 @@ repos: - id: rst-inline-touching-normal - repo: https://github.com/jendrikseipp/vulture - rev: "v2.10" + rev: "v2.11" hooks: - id: vulture args: @@ -104,7 +104,7 @@ repos: - "tests" - repo: https://github.com/pycqa/bandit - rev: 1.7.6 + rev: 1.7.8 hooks: - id: bandit args: @@ -118,7 +118,7 @@ repos: # GHA linting - repo: https://github.com/python-jsonschema/check-jsonschema - rev: "0.27.3" + rev: "0.28.0" hooks: - id: check-github-workflows - id: check-readthedocs From 585a0983198fcf5ced9a1a337e521cbd8f4d5de4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 18 Mar 2024 18:29:23 +0000 Subject: [PATCH 149/167] style: beautify d59f5931e6b7fac3604f9d5f13a1947688b285d9 --- semantic_release/__init__.py | 1 + semantic_release/cli/util.py | 1 + semantic_release/commit_parser/_base.py | 3 +-- semantic_release/commit_parser/angular.py | 1 + semantic_release/commit_parser/emoji.py | 1 + semantic_release/commit_parser/scipy.py | 1 + semantic_release/commit_parser/tag.py | 1 + semantic_release/hvcs/_base.py | 1 + semantic_release/hvcs/gitea.py | 1 + semantic_release/hvcs/github.py | 1 + semantic_release/hvcs/gitlab.py | 1 + semantic_release/version/version.py | 6 ++--- tests/command_line/conftest.py | 6 ++--- tests/conftest.py | 4 ++-- tests/fixtures/example_project.py | 15 ++++-------- tests/fixtures/git_repo.py | 28 ++++++++++------------- 16 files changed, 34 insertions(+), 38 deletions(-) diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 14fca7015..c7f1268e8 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -1,4 +1,5 @@ """Python Semantic Release""" + from __future__ import annotations from semantic_release.commit_parser import ( diff --git a/semantic_release/cli/util.py b/semantic_release/cli/util.py index 71acdef6e..c1c01b79c 100644 --- a/semantic_release/cli/util.py +++ b/semantic_release/cli/util.py @@ -1,4 +1,5 @@ """Utilities for command-line functionality""" + from __future__ import annotations import json diff --git a/semantic_release/commit_parser/_base.py b/semantic_release/commit_parser/_base.py index c41b66f61..c104ee579 100644 --- a/semantic_release/commit_parser/_base.py +++ b/semantic_release/commit_parser/_base.py @@ -71,5 +71,4 @@ def __init__(self, options: _OPTS) -> None: self.options = options @abstractmethod - def parse(self, commit: Commit) -> _TT: - ... + def parse(self, commit: Commit) -> _TT: ... diff --git a/semantic_release/commit_parser/angular.py b/semantic_release/commit_parser/angular.py index e4433ac9c..92a391d15 100644 --- a/semantic_release/commit_parser/angular.py +++ b/semantic_release/commit_parser/angular.py @@ -2,6 +2,7 @@ Angular commit style parser https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines """ + from __future__ import annotations import logging diff --git a/semantic_release/commit_parser/emoji.py b/semantic_release/commit_parser/emoji.py index 25d43f54b..f0ab4f218 100644 --- a/semantic_release/commit_parser/emoji.py +++ b/semantic_release/commit_parser/emoji.py @@ -1,4 +1,5 @@ """Commit parser which looks for emojis to determine the type of commit""" + import logging from typing import Tuple diff --git a/semantic_release/commit_parser/scipy.py b/semantic_release/commit_parser/scipy.py index 9cb51953e..0503cfa2b 100644 --- a/semantic_release/commit_parser/scipy.py +++ b/semantic_release/commit_parser/scipy.py @@ -34,6 +34,7 @@ .. _`scipy-style`: https://docs.scipy.org/doc/scipy/reference/dev/contributor/development_workflow.html#writing-the-commit-message """ + from __future__ import annotations import logging diff --git a/semantic_release/commit_parser/tag.py b/semantic_release/commit_parser/tag.py index b6e592539..dc210c5b5 100644 --- a/semantic_release/commit_parser/tag.py +++ b/semantic_release/commit_parser/tag.py @@ -1,4 +1,5 @@ """Legacy commit parser from Python Semantic Release 1.0""" + import logging import re diff --git a/semantic_release/hvcs/_base.py b/semantic_release/hvcs/_base.py index 3a825a1d7..c773e3570 100644 --- a/semantic_release/hvcs/_base.py +++ b/semantic_release/hvcs/_base.py @@ -1,4 +1,5 @@ """Common functionality and interface for interacting with Git remote VCS""" + from __future__ import annotations import logging diff --git a/semantic_release/hvcs/gitea.py b/semantic_release/hvcs/gitea.py index 0138020ee..23adc3594 100644 --- a/semantic_release/hvcs/gitea.py +++ b/semantic_release/hvcs/gitea.py @@ -1,4 +1,5 @@ """Helper code for interacting with a Gitea remote VCS""" + from __future__ import annotations import glob diff --git a/semantic_release/hvcs/github.py b/semantic_release/hvcs/github.py index a147fb91c..a29a606ee 100644 --- a/semantic_release/hvcs/github.py +++ b/semantic_release/hvcs/github.py @@ -1,4 +1,5 @@ """Helper code for interacting with a GitHub remote VCS""" + from __future__ import annotations import glob diff --git a/semantic_release/hvcs/gitlab.py b/semantic_release/hvcs/gitlab.py index 5e23e5bae..060e3934d 100644 --- a/semantic_release/hvcs/gitlab.py +++ b/semantic_release/hvcs/gitlab.py @@ -1,4 +1,5 @@ """Helper code for interacting with a Gitlab remote VCS""" + from __future__ import annotations import logging diff --git a/semantic_release/version/version.py b/semantic_release/version/version.py index de0b879a8..41ec5e107 100644 --- a/semantic_release/version/version.py +++ b/semantic_release/version/version.py @@ -25,15 +25,13 @@ def _comparator( *, type_guard: bool, -) -> Callable[[VersionComparator], VersionComparator]: - ... +) -> Callable[[VersionComparator], VersionComparator]: ... @overload def _comparator( method: VersionComparator, *, type_guard: bool = True -) -> VersionComparator: - ... +) -> VersionComparator: ... def _comparator( diff --git a/tests/command_line/conftest.py b/tests/command_line/conftest.py index 85c5efdf1..65b8c6b57 100644 --- a/tests/command_line/conftest.py +++ b/tests/command_line/conftest.py @@ -31,14 +31,12 @@ class ReadConfigFileFn(Protocol): """Read the raw config file from `config_path`.""" - def __call__(self, file: Path | str) -> RawConfig: - ... + def __call__(self, file: Path | str) -> RawConfig: ... class RetrieveRuntimeContextFn(Protocol): """Retrieve the runtime context for a repo.""" - def __call__(self, repo: Repo) -> RuntimeContext: - ... + def __call__(self, repo: Repo) -> RuntimeContext: ... @pytest.fixture diff --git a/tests/conftest.py b/tests/conftest.py index 05f70cbff..bff679153 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ """Note: fixtures are stored in the tests/fixtures directory for better organisation""" + from __future__ import annotations from pathlib import Path @@ -13,8 +14,7 @@ from typing import Generator, Protocol class TeardownCachedDirFn(Protocol): - def __call__(self, directory: Path) -> Path: - ... + def __call__(self, directory: Path) -> Path: ... @pytest.fixture(scope="session") diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index c1ea93191..ff7a70d88 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -38,24 +38,19 @@ ExProjectDir = Path class SetFlagFn(Protocol): - def __call__(self, flag: bool) -> None: - ... + def __call__(self, flag: bool) -> None: ... class UpdatePyprojectTomlFn(Protocol): - def __call__(self, setting: str, value: Any) -> None: - ... + def __call__(self, setting: str, value: Any) -> None: ... class UseHvcsFn(Protocol): - def __call__(self, domain: str | None = None) -> type[HvcsBase]: - ... + def __call__(self, domain: str | None = None) -> type[HvcsBase]: ... class UseParserFn(Protocol): - def __call__(self) -> type[CommitParser]: - ... + def __call__(self) -> type[CommitParser]: ... class UseReleaseNotesTemplateFn(Protocol): - def __call__(self) -> None: - ... + def __call__(self) -> None: ... @pytest.fixture(scope="session") diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index c0b417d1e..65ad7ffbd 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -69,30 +69,26 @@ def __call__( hvcs_domain: str = ..., tag_format_str: str | None = None, extra_configs: dict[str, TomlSerializableTypes] | None = None, - ) -> tuple[Path, HvcsBase]: - ... + ) -> tuple[Path, HvcsBase]: ... class CommitNReturnChangelogEntryFn(Protocol): - def __call__(self, git_repo: Repo, commit_msg: str, hvcs: HvcsBase) -> str: - ... + def __call__(self, git_repo: Repo, commit_msg: str, hvcs: HvcsBase) -> str: ... class SimulateChangeCommitsNReturnChangelogEntryFn(Protocol): def __call__( self, git_repo: Repo, commit_msgs: list[CommitMsg], hvcs: HvcsBase - ) -> list[CommitMsg]: - ... + ) -> list[CommitMsg]: ... class CreateReleaseFn(Protocol): - def __call__(self, git_repo: Repo, version: str, tag_format: str = ...) -> None: - ... + def __call__( + self, git_repo: Repo, version: str, tag_format: str = ... + ) -> None: ... class ExProjectGitRepoFn(Protocol): - def __call__(self) -> Repo: - ... + def __call__(self) -> Repo: ... class GetVersionStringsFn(Protocol): - def __call__(self) -> list[VersionStr]: - ... + def __call__(self) -> list[VersionStr]: ... RepoDefinition = dict[VersionStr, RepoVersionDef] """ @@ -101,16 +97,16 @@ def __call__(self) -> list[VersionStr]: """ class GetRepoDefinitionFn(Protocol): - def __call__(self, commit_type: CommitConvention = "angular") -> RepoDefinition: - ... + def __call__( + self, commit_type: CommitConvention = "angular" + ) -> RepoDefinition: ... class SimulateDefaultChangelogCreationFn(Protocol): def __call__( self, repo_definition: RepoDefinition, dest_file: Path | None = None, - ) -> str: - ... + ) -> str: ... @pytest.fixture(scope="session") From 04bbefdebcdcf07667c2c297f1ce516ad35a1733 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 10 Feb 2024 21:11:07 -0500 Subject: [PATCH 150/167] refactor: drop other 3.7 references --- docs/commit-parsing.rst | 2 +- semantic_release/changelog/release_history.py | 14 +++++--------- semantic_release/changelog/template.py | 4 +++- semantic_release/cli/config.py | 6 ++++-- tests/util.py | 1 + 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/commit-parsing.rst b/docs/commit-parsing.rst index 0c6442c66..b1c525e9c 100644 --- a/docs/commit-parsing.rst +++ b/docs/commit-parsing.rst @@ -284,7 +284,7 @@ available. .. _Rust's error handling: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html .. _black: https://github.com/psf/black/blob/main/src/black/rusty.py .. _catching exceptions in Python is slower: https://docs.python.org/3/faq/design.html#how-fast-are-exceptions -.. _namedtuple: https://docs.python.org/3.7/library/typing.html#typing.NamedTuple +.. _namedtuple: https://docs.python.org/3/library/typing.html#typing.NamedTuple .. _commit-parsing-parser-options: diff --git a/semantic_release/changelog/release_history.py b/semantic_release/changelog/release_history.py index f34fc3e51..8d23e8bcd 100644 --- a/semantic_release/changelog/release_history.py +++ b/semantic_release/changelog/release_history.py @@ -3,20 +3,16 @@ import logging from collections import defaultdict from datetime import datetime, timedelta, timezone -from typing import TYPE_CHECKING, Iterable, Iterator +from typing import TYPE_CHECKING, TypedDict from git.objects.tag import TagObject -# For Python3.7 compatibility -from typing_extensions import TypedDict - -from semantic_release.commit_parser import ( - ParseError, -) +from semantic_release.commit_parser import ParseError from semantic_release.version.algorithm import tags_and_versions if TYPE_CHECKING: - import re + from re import Pattern + from typing import Iterable, Iterator from git.repo.base import Repo from git.util import Actor @@ -39,7 +35,7 @@ def from_git_history( repo: Repo, translator: VersionTranslator, commit_parser: CommitParser[ParseResult, ParserOptions], - exclude_commit_patterns: Iterable[re.Pattern[str]] = (), + exclude_commit_patterns: Iterable[Pattern[str]] = (), ) -> ReleaseHistory: all_git_tags_and_versions = tags_and_versions(repo.tags, translator) unreleased: dict[str, list[ParseResult]] = defaultdict(list) diff --git a/semantic_release/changelog/template.py b/semantic_release/changelog/template.py index 906bfc876..cefc9accd 100644 --- a/semantic_release/changelog/template.py +++ b/semantic_release/changelog/template.py @@ -12,8 +12,10 @@ from semantic_release.helpers import dynamic_import if TYPE_CHECKING: + from typing import Literal + from jinja2 import Environment - from typing_extensions import Literal + log = logging.getLogger(__name__) diff --git a/semantic_release/cli/config.py b/semantic_release/cli/config.py index 39229f1c0..8a414fedc 100644 --- a/semantic_release/cli/config.py +++ b/semantic_release/cli/config.py @@ -7,13 +7,15 @@ from dataclasses import dataclass, is_dataclass from enum import Enum from pathlib import Path -from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type, Union +from typing import Any, ClassVar, Dict, List, Literal, Optional, Tuple, Type, Union from git import Actor from git.repo.base import Repo from jinja2 import Environment from pydantic import BaseModel, Field, RootModel, ValidationError, model_validator -from typing_extensions import Annotated, Literal + +# For Python 3.8 compatibility +from typing_extensions import Annotated from semantic_release import hvcs from semantic_release.changelog import environment diff --git a/tests/util.py b/tests/util.py index fe1d24f80..8c2b7d075 100644 --- a/tests/util.py +++ b/tests/util.py @@ -25,6 +25,7 @@ try: from typing import TypeAlias except ImportError: + # for python 3.8 and 3.9 from typing_extensions import TypeAlias from unittest.mock import MagicMock From fc74ef20e92645a9939efb0fcea69254a6a89681 Mon Sep 17 00:00:00 2001 From: andrew-kline <65789862+andrew-kline@users.noreply.github.com> Date: Tue, 27 Feb 2024 21:37:59 -0800 Subject: [PATCH 151/167] test(helpers): add git url parse cases for urls with special characters --- tests/unit/semantic_release/test_helpers.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/unit/semantic_release/test_helpers.py b/tests/unit/semantic_release/test_helpers.py index ee8ffdee3..e7db7adfd 100644 --- a/tests/unit/semantic_release/test_helpers.py +++ b/tests/unit/semantic_release/test_helpers.py @@ -61,6 +61,15 @@ "myproject", ), ), + ( + "first.last_test-1@subsubdomain.subdomain.company-net.com:username/myproject.git", + ParsedGitUrl( + "ssh", + "first.last_test-1@subsubdomain.subdomain.company-net.com", + "username", + "myproject", + ), + ), ( "ssh://git@github.com:3759/myproject.git", ParsedGitUrl("ssh", "git@github.com", "3759", "myproject"), From 27cd93a0a65ee3787ca51be4c91c48f6ddb4269c Mon Sep 17 00:00:00 2001 From: andrew-kline <65789862+andrew-kline@users.noreply.github.com> Date: Tue, 27 Feb 2024 21:39:33 -0800 Subject: [PATCH 152/167] fix(parse-git-url): handle urls with url-safe special characters --- semantic_release/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/semantic_release/helpers.py b/semantic_release/helpers.py index 482413e48..3725ddfa5 100644 --- a/semantic_release/helpers.py +++ b/semantic_release/helpers.py @@ -114,12 +114,12 @@ def parse_git_url(url: str) -> ParsedGitUrl: # Normalizers are a list of tuples of (pattern, replacement) normalizers = [ # normalize implicit ssh urls to explicit ssh:// - (r"^(\w+@)", r"ssh://\1"), + (r"^([\w._-]+@)", r"ssh://\1"), # normalize git+ssh:// urls to ssh:// (r"^git\+ssh://", "ssh://"), # normalize an scp like syntax to URL compatible syntax # excluding port definitions (:#####) & including numeric usernames - (r"(ssh://(?:\w+@)?[\w.-]+):(?!\d{1,5}/\w+/)(.*)$", r"\1/\2"), + (r"(ssh://(?:[\w._-]+@)?[\w.-]+):(?!\d{1,5}/\w+/)(.*)$", r"\1/\2"), # normalize implicit file (windows || posix) urls to explicit file:// urls (r"^([C-Z]:/)|^/(\w)", r"file:///\1\2"), ] From ca1c022d3b2dd6b968dbe9d8829278681536946f Mon Sep 17 00:00:00 2001 From: semantic-release Date: Tue, 19 Mar 2024 05:00:47 +0000 Subject: [PATCH 153/167] 9.2.1 Automatically generated by python-semantic-release --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb44f4ed..cf9fb2c62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ +## v9.2.1 (2024-03-19) + +### Chore + +* chore(pre-commit): upgrade hooks for pyupgrade, mypy, vulture, bandit, and check-jsonschema ([`d59f593`](https://github.com/python-semantic-release/python-semantic-release/commit/d59f5931e6b7fac3604f9d5f13a1947688b285d9)) + +* chore(deps-dev): bump ruff from 0.1.11 to 0.3.2 ([`ccdc976`](https://github.com/python-semantic-release/python-semantic-release/commit/ccdc9763b2f588bafe7a8c7d71342d4ebcb09f13)) + +* chore(pre-commit): cleanup ruff warnings ([`4cb4ca1`](https://github.com/python-semantic-release/python-semantic-release/commit/4cb4ca19dc3d649a54e7a1b1e9a44a196624eef0)) + +* chore(pre-commit): cleanup 'check blanket type ignore' warnings ([`7e938e6`](https://github.com/python-semantic-release/python-semantic-release/commit/7e938e69af7d45c6d77dcf0a3627e5663cb35bff)) + +* chore(pre-commit): exclude generated file CHANGELOG.md from checks ([`6e1f5cb`](https://github.com/python-semantic-release/python-semantic-release/commit/6e1f5cbd1c01f9a8c6cd8bafd706e5b5ec6ab007)) + +* chore(pre-commit): cleanup vulture warnings ([`4b2467a`](https://github.com/python-semantic-release/python-semantic-release/commit/4b2467a1d7b14cd1afc3b355e69e81804fcc5b4d)) + +### Fix + +* fix(parse-git-url): handle urls with url-safe special characters ([`27cd93a`](https://github.com/python-semantic-release/python-semantic-release/commit/27cd93a0a65ee3787ca51be4c91c48f6ddb4269c)) + +### Refactor + +* refactor: drop other 3.7 references ([`04bbefd`](https://github.com/python-semantic-release/python-semantic-release/commit/04bbefdebcdcf07667c2c297f1ce516ad35a1733)) + +### Style + +* style: beautify d59f5931e6b7fac3604f9d5f13a1947688b285d9 ([`585a098`](https://github.com/python-semantic-release/python-semantic-release/commit/585a0983198fcf5ced9a1a337e521cbd8f4d5de4)) + +### Test + +* test(helpers): add git url parse cases for urls with special characters ([`fc74ef2`](https://github.com/python-semantic-release/python-semantic-release/commit/fc74ef20e92645a9939efb0fcea69254a6a89681)) + + ## v9.2.0 (2024-03-18) ### Build diff --git a/pyproject.toml b/pyproject.toml index f57aeb7d2..b03656c70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.2.0" +version = "9.2.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index c7f1268e8..7842e7350 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.2.0" +__version__ = "9.2.1" def setup_hook(argv: list[str]) -> None: From 1d538790a727852d61c5edfe91c5707ed3b6af5a Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 9 Mar 2024 21:48:17 -0500 Subject: [PATCH 154/167] test(cli-help): add test cases of --help failures --- tests/command_line/test_help.py | 219 ++++++++++++++++++++++++++++++-- 1 file changed, 210 insertions(+), 9 deletions(-) diff --git a/tests/command_line/test_help.py b/tests/command_line/test_help.py index ff351615e..a80a7aee2 100644 --- a/tests/command_line/test_help.py +++ b/tests/command_line/test_help.py @@ -1,21 +1,222 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + import pytest +import semantic_release from semantic_release.cli import changelog, generate_config, main, publish, version +if TYPE_CHECKING: + from click import Command + from click.testing import CliRunner + from git import Repo + + from tests.fixtures import UpdatePyprojectTomlFn + + +# Define the expected exit code for the help command +help_exit_code = 0 + + +@pytest.mark.parametrize( + "help_option", ("-h", "--help"), ids=lambda opt: opt.lstrip("-") +) +@pytest.mark.parametrize( + "command", + (main, changelog, generate_config, publish, version), + ids=lambda cmd: cmd.name, +) +def test_help_no_repo( + help_option: str, + command: Command, + cli_runner: CliRunner, + change_to_ex_proj_dir: None, +): + """ + Test that the help message is displayed even when the current directory is not a git repository + and there is not a configuration file available. + Documented issue #840 + """ + # Generate some expected output that should be specific per command + cmd_usage = str.join( + " ", + list( + filter( + None, + [ + "Usage:", + semantic_release.__name__, + command.name if command.name != "main" else "", + "[OPTIONS]", + "" if command.name != main.name else "COMMAND [ARGS]...", + ], + ) + ), + ) + + # Create the arguments list for subcommands unless its main + args = list( + filter(None, [command.name if command.name != main.name else "", help_option]) + ) + + # Run the command with the help option + result = cli_runner.invoke(main, args, prog_name=semantic_release.__name__) + + # Evaluate result + assert help_exit_code == result.exit_code + assert cmd_usage in result.output + -@pytest.mark.parametrize("help_option", ("-h", "--help")) +@pytest.mark.parametrize( + "help_option", ("-h", "--help"), ids=lambda opt: opt.lstrip("-") +) @pytest.mark.parametrize( "command", (main, changelog, generate_config, publish, version), ids=lambda cmd: cmd.name, ) -def test_help(help_option, command, cli_runner): - result = cli_runner.invoke(command, [help_option]) +def test_help_valid_config( + help_option: str, + command: Command, + cli_runner: CliRunner, + repo_with_single_branch_angular_commits: Repo, +): + """ + Test that the help message is displayed when the current directory is a git repository + and there is a valid configuration file available. + Documented issue #840 + """ + cmd_usage = str.join( + " ", + list( + filter( + None, + [ + "Usage:", + semantic_release.__name__, + command.name if command.name != main.name else "", + "[OPTIONS]", + "" if command.name != main.name else "COMMAND [ARGS]...", + ], + ) + ), + ) + + # Create the arguments list for subcommands unless its main + args = list( + filter(None, [command.name if command.name != main.name else "", help_option]) + ) + + # Run the command with the help option + result = cli_runner.invoke(main, args, prog_name=semantic_release.__name__) + + # Evaluate result + assert help_exit_code == result.exit_code + assert cmd_usage in result.output + + +@pytest.mark.parametrize( + "help_option", ("-h", "--help"), ids=lambda opt: opt.lstrip("-") +) +@pytest.mark.parametrize( + "command", + (main, changelog, generate_config, publish, version), + ids=lambda cmd: cmd.name, +) +def test_help_invalid_config( + help_option: str, + command: Command, + cli_runner: CliRunner, + repo_with_single_branch_angular_commits: Repo, + update_pyproject_toml: UpdatePyprojectTomlFn, +): + """ + Test that the help message is displayed when the current directory is a git repository + and there is an invalid configuration file available. + Documented issue #840 + """ + # Update the configuration file to have an invalid value + update_pyproject_toml("tool.semantic_release.remote.type", "invalidhvcs") + + # Generate some expected output that should be specific per command + cmd_usage = str.join( + " ", + list( + filter( + None, + [ + "Usage:", + semantic_release.__name__, + command.name if command.name != "main" else "", + "[OPTIONS]", + "" if command.name != main.name else "COMMAND [ARGS]...", + ], + ) + ), + ) + + # Create the arguments list for subcommands unless its main + args = list( + filter(None, [command.name if command.name != main.name else "", help_option]) + ) + + # Run the command with the help option + result = cli_runner.invoke(main, args, prog_name=semantic_release.__name__) + + # Evaluate result + assert help_exit_code == result.exit_code + assert cmd_usage in result.output + + +@pytest.mark.parametrize( + "help_option", ("-h", "--help"), ids=lambda opt: opt.lstrip("-") +) +@pytest.mark.parametrize( + "command", + (main, changelog, generate_config, publish, version), + ids=lambda cmd: cmd.name, +) +def test_help_non_release_branch( + help_option: str, + command: Command, + cli_runner: CliRunner, + repo_with_single_branch_angular_commits: Repo, +): + """ + Test that the help message is displayed even when the current branch is not a release branch. + Documented issue #840 + """ + # Create & checkout a non-release branch + repo = repo_with_single_branch_angular_commits + non_release_branch = repo.create_head("feature-branch") + non_release_branch.checkout() + + # Generate some expected output that should be specific per command + cmd_usage = str.join( + " ", + list( + filter( + None, + [ + "Usage:", + semantic_release.__name__, + command.name if command.name != "main" else "", + "[OPTIONS]", + "" if command.name != main.name else "COMMAND [ARGS]...", + ], + ) + ), + ) + + # Create the arguments list for subcommands unless its main + args = list( + filter(None, [command.name if command.name != main.name else "", help_option]) + ) - assert result.exit_code == 0 - # commands have help text - assert result.output + # Run the command with the help option + result = cli_runner.invoke(main, args, prog_name=semantic_release.__name__) - if command is not main: - # commands have their own unique help text - assert result.output != cli_runner.invoke(main, [help_option]).output + # Evaluate result + assert help_exit_code == result.exit_code + assert cmd_usage in result.output From 91d221a01266e5ca6de5c73296b0a90987847494 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 9 Mar 2024 15:42:46 -0500 Subject: [PATCH 155/167] fix(cli): enable subcommand help even if config is invalid Refactors configuration loading to use lazy loading by subcommands triggered by the property access of the runtime_ctx object. Resolves the issues when running `--help` on subcommands when a configuration is invalid Resolves: #840 --- semantic_release/cli/commands/changelog.py | 9 +- semantic_release/cli/commands/cli_context.py | 100 ++++++++++++++++++ .../cli/commands/generate_config.py | 2 +- semantic_release/cli/commands/main.py | 84 +++------------ semantic_release/cli/commands/publish.py | 14 ++- semantic_release/cli/commands/version.py | 37 ++++--- semantic_release/cli/config.py | 15 +-- 7 files changed, 161 insertions(+), 100 deletions(-) create mode 100644 semantic_release/cli/commands/cli_context.py diff --git a/semantic_release/cli/commands/changelog.py b/semantic_release/cli/commands/changelog.py index 785f0a149..b2d13918c 100644 --- a/semantic_release/cli/commands/changelog.py +++ b/semantic_release/cli/commands/changelog.py @@ -16,7 +16,7 @@ from semantic_release.cli.util import noop_report if TYPE_CHECKING: - from semantic_release.cli.config import RuntimeContext + from semantic_release.cli.commands.cli_context import CliContextObj from semantic_release.version import Version log = logging.getLogger(__name__) @@ -34,10 +34,11 @@ default=None, help="Post the generated release notes to the remote VCS's release for this tag", ) -@click.pass_context -def changelog(ctx: click.Context, release_tag: str | None = None) -> None: +@click.pass_obj +def changelog(cli_ctx: CliContextObj, release_tag: str | None = None) -> None: """Generate and optionally publish a changelog for your project""" - runtime: RuntimeContext = ctx.obj + ctx = click.get_current_context() + runtime = cli_ctx.runtime_ctx repo = runtime.repo parser = runtime.commit_parser translator = runtime.version_translator diff --git a/semantic_release/cli/commands/cli_context.py b/semantic_release/cli/commands/cli_context.py new file mode 100644 index 000000000..7f71dc73b --- /dev/null +++ b/semantic_release/cli/commands/cli_context.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +import logging +from pathlib import Path +from typing import TYPE_CHECKING + +import click +from click.core import ParameterSource +from git import InvalidGitRepositoryError +from pydantic import ValidationError + +from semantic_release.cli.config import ( + RawConfig, + RuntimeContext, +) +from semantic_release.cli.util import load_raw_config_file, rprint +from semantic_release.errors import InvalidConfiguration, NotAReleaseBranch + +if TYPE_CHECKING: + from semantic_release.cli.config import GlobalCommandLineOptions + + class CliContext(click.Context): + obj: CliContextObj + + +class CliContextObj: + def __init__( + self, + ctx: click.Context, + logger: logging.Logger, + global_opts: GlobalCommandLineOptions, + ) -> None: + self._runtime_ctx: RuntimeContext | None = None + self.ctx = ctx + self.logger = logger + self.global_opts = global_opts + + @property + def runtime_ctx(self) -> RuntimeContext: + """ + Lazy load the runtime context. This is done to avoid configuration loading when + the command is not run. This is useful for commands like `--help` and `--version` + """ + if self._runtime_ctx is None: + self._runtime_ctx = self._init_runtime_ctx() + return self._runtime_ctx + + def _init_runtime_ctx(self) -> RuntimeContext: + config_path = Path(self.global_opts.config_file) + conf_file_exists = config_path.exists() + was_conf_file_user_provided = bool( + self.ctx.get_parameter_source("config_file") + not in ( + ParameterSource.DEFAULT, + ParameterSource.DEFAULT_MAP, + ) + ) + + try: + if was_conf_file_user_provided and not conf_file_exists: + raise FileNotFoundError( + f"File {self.global_opts.config_file} does not exist" + ) + + config_obj = ( + {} if not conf_file_exists else load_raw_config_file(config_path) + ) + if not config_obj: + self.logger.info( + "configuration empty, falling back to default configuration" + ) + + raw_config = RawConfig.model_validate(config_obj) + runtime = RuntimeContext.from_raw_config( + raw_config, + global_cli_options=self.global_opts, + ) + except NotAReleaseBranch as exc: + rprint(f"[bold {'red' if self.global_opts.strict else 'orange1'}]{exc!s}") + # If not strict, exit 0 so other processes can continue. For example, in + # multibranch CI it might be desirable to run a non-release branch's pipeline + # without specifying conditional execution of PSR based on branch name + self.ctx.exit(2 if self.global_opts.strict else 0) + except FileNotFoundError as exc: + click.echo(str(exc), err=True) + self.ctx.exit(2) + except ( + ValidationError, + InvalidConfiguration, + InvalidGitRepositoryError, + ) as exc: + click.echo(str(exc), err=True) + self.ctx.exit(1) + + # This allows us to mask secrets in the logging + # by applying it to all the configured handlers + for handler in logging.getLogger().handlers: + handler.addFilter(runtime.masker) + + return runtime diff --git a/semantic_release/cli/commands/generate_config.py b/semantic_release/cli/commands/generate_config.py index b571728c8..a6bf36013 100644 --- a/semantic_release/cli/commands/generate_config.py +++ b/semantic_release/cli/commands/generate_config.py @@ -38,7 +38,7 @@ def generate_config(fmt: str = "toml", is_pyproject_toml: bool = False) -> None: your needs. For example, to append the default configuration to your pyproject.toml file, you can use the following command: - semantic-release generate-config -f toml >> pyproject.toml + semantic-release generate-config --pyproject >> pyproject.toml """ # due to possible IntEnum values (which are not supported by tomlkit.dumps, see sdispater/tomlkit#237), # we must ensure the transformation of the model to a dict uses json serializable values diff --git a/semantic_release/cli/commands/main.py b/semantic_release/cli/commands/main.py index 13d1cc13f..90d1b5457 100644 --- a/semantic_release/cli/commands/main.py +++ b/semantic_release/cli/commands/main.py @@ -1,26 +1,21 @@ from __future__ import annotations import logging -from pathlib import Path +# from typing import TYPE_CHECKING import click -from click.core import ParameterSource -from git import InvalidGitRepositoryError -from git.repo.base import Repo -from pydantic import ValidationError from rich.console import Console from rich.logging import RichHandler import semantic_release -from semantic_release.cli.commands.generate_config import generate_config -from semantic_release.cli.config import ( - GlobalCommandLineOptions, - RawConfig, - RuntimeContext, -) +from semantic_release.cli.commands.cli_context import CliContextObj +from semantic_release.cli.config import GlobalCommandLineOptions from semantic_release.cli.const import DEFAULT_CONFIG_FILE -from semantic_release.cli.util import load_raw_config_file, rprint -from semantic_release.errors import InvalidConfiguration, NotAReleaseBranch +from semantic_release.cli.util import rprint + +# if TYPE_CHECKING: +# pass + FORMAT = "[%(name)s] %(levelname)s %(module)s.%(funcName)s: %(message)s" @@ -94,19 +89,8 @@ def main( ], ) - log = logging.getLogger(__name__) - - if ctx.invoked_subcommand == generate_config.name: - # generate-config doesn't require any of the usual setup, - # so exit out early and delegate to it - log.debug("Forwarding to %s", generate_config.name) - return - - log.debug("logging level set to: %s", logging.getLevelName(log_level)) - try: - repo = Repo(".", search_parent_directories=True) - except InvalidGitRepositoryError: - ctx.fail("Not in a valid Git repository") + logger = logging.getLogger(__name__) + logger.debug("logging level set to: %s", logging.getLevelName(log_level)) if noop: rprint( @@ -115,51 +99,9 @@ def main( ) cli_options = GlobalCommandLineOptions( - noop=noop, - verbosity=verbosity, - config_file=config_file, - strict=strict, + noop=noop, verbosity=verbosity, config_file=config_file, strict=strict ) - log.debug("global cli options: %s", cli_options) - - config_path = Path(config_file) - # default no config loaded - config_text = {} - if not config_path.exists(): - if ctx.get_parameter_source("config_file") not in ( - ParameterSource.DEFAULT, - ParameterSource.DEFAULT_MAP, - ): - ctx.fail(f"File {config_file} does not exist") - - log.info( - "configuration file %s not found, using default configuration", - config_file, - ) - else: - try: - config_text = load_raw_config_file(config_path) - except InvalidConfiguration as exc: - ctx.fail(str(exc)) + logger.debug("global cli options: %s", cli_options) - try: - raw_config = RawConfig.model_validate(config_text) - runtime = RuntimeContext.from_raw_config( - raw_config, repo=repo, global_cli_options=cli_options - ) - except NotAReleaseBranch as exc: - rprint(f"[bold {'red' if strict else 'orange1'}]{exc!s}") - # If not strict, exit 0 so other processes can continue. For example, in - # multibranch CI it might be desirable to run a non-release branch's pipeline - # without specifying conditional execution of PSR based on branch name - ctx.exit(2 if strict else 0) - except (ValidationError, InvalidConfiguration) as exc: - click.echo(str(exc), err=True) - ctx.exit(1) - ctx.obj = runtime - - # This allows us to mask secrets in the logging - # by applying it to all the configured handlers - for handler in logging.getLogger().handlers: - handler.addFilter(runtime.masker) + ctx.obj = CliContextObj(ctx, logger, cli_options) diff --git a/semantic_release/cli/commands/publish.py b/semantic_release/cli/commands/publish.py index 380946302..50a9478df 100644 --- a/semantic_release/cli/commands/publish.py +++ b/semantic_release/cli/commands/publish.py @@ -1,10 +1,17 @@ +from __future__ import annotations + import logging +from typing import TYPE_CHECKING import click from semantic_release.cli.util import noop_report from semantic_release.version import tags_and_versions +if TYPE_CHECKING: + from semantic_release.cli.commands.cli_context import CliContextObj + + log = logging.getLogger(__name__) @@ -20,10 +27,11 @@ help="The tag associated with the release to publish to", default="latest", ) -@click.pass_context -def publish(ctx: click.Context, tag: str = "latest") -> None: +@click.pass_obj +def publish(cli_ctx: CliContextObj, tag: str = "latest") -> None: """Build and publish a distribution to a VCS release.""" - runtime = ctx.obj + ctx = click.get_current_context() + runtime = cli_ctx.runtime_ctx repo = runtime.repo hvcs_client = runtime.hvcs_client translator = runtime.version_translator diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index 5a9bbb6d5..f43983c15 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -5,7 +5,7 @@ import subprocess from contextlib import nullcontext from datetime import datetime -from typing import TYPE_CHECKING, ContextManager, Iterable +from typing import TYPE_CHECKING import click import shellingham # type: ignore[import] @@ -28,10 +28,12 @@ log = logging.getLogger(__name__) if TYPE_CHECKING: # pragma: no cover + from typing import ContextManager, Iterable + from git import Repo from git.refs.tag import Tag - from semantic_release.cli.config import RuntimeContext + from semantic_release.cli.commands.cli_context import CliContextObj from semantic_release.version import VersionTranslator from semantic_release.version.declaration import VersionDeclarationABC @@ -213,9 +215,9 @@ def shell(cmd: str, *, check: bool = True) -> subprocess.CompletedProcess: is_flag=True, help="Skip building the current project", ) -@click.pass_context +@click.pass_obj def version( # noqa: C901 - ctx: click.Context, + cli_ctx: CliContextObj, print_only: bool = False, print_only_tag: bool = False, print_last_released: bool = False, @@ -231,22 +233,27 @@ def version( # noqa: C901 build_metadata: str | None = None, skip_build: bool = False, ) -> str: - r""" + """ Detect the semantically correct next version that should be applied to your project. - \b By default: - * Write this new version to the project metadata locations - specified in the configuration file - * Create a new commit with these locations and any other assets configured - to be included in a release - * Tag this commit according the configured format, with a tag that uniquely - identifies the version being released. - * Push the new tag and commit to the remote for the repository - * Create a release (if supported) in the remote VCS for this tag + + * Write this new version to the project metadata locations specified + in the configuration file + + * Create a new commit with these locations and any other assets configured + to be included in a release + + * Tag this commit according the configured format, with a tag that uniquely + identifies the version being released. + + * Push the new tag and commit to the remote for the repository + + * Create a release (if supported) in the remote VCS for this tag """ - runtime: RuntimeContext = ctx.obj + ctx = click.get_current_context() + runtime = cli_ctx.runtime_ctx repo = runtime.repo translator = runtime.version_translator diff --git a/semantic_release/cli/config.py b/semantic_release/cli/config.py index 8a414fedc..890467e61 100644 --- a/semantic_release/cli/config.py +++ b/semantic_release/cli/config.py @@ -9,13 +9,13 @@ from pathlib import Path from typing import Any, ClassVar, Dict, List, Literal, Optional, Tuple, Type, Union -from git import Actor +from git import Actor, InvalidGitRepositoryError from git.repo.base import Repo from jinja2 import Environment from pydantic import BaseModel, Field, RootModel, ValidationError, model_validator -# For Python 3.8 compatibility -from typing_extensions import Annotated +# For Python 3.8, 3.9, 3.10 compatibility +from typing_extensions import Annotated, Self from semantic_release import hvcs from semantic_release.changelog import environment @@ -119,7 +119,7 @@ class RemoteConfig(BaseModel): ignore_token_for_push: bool = False @model_validator(mode="after") - def set_default_token(self) -> RemoteConfig: + def set_default_token(self) -> Self: # Set the default token name for the given VCS when no user input is given if not self.token and self.type in _known_hvcs: default_token_name = _known_hvcs[self.type].DEFAULT_ENV_TOKEN_NAME @@ -155,7 +155,7 @@ class RawConfig(BaseModel): version_variables: Optional[Tuple[str, ...]] = None @model_validator(mode="after") - def set_default_opts(self) -> RawConfig: + def set_default_opts(self) -> Self: # Set the default parser options for the given commit parser when no user input is given if not self.commit_parser_options and self.commit_parser: parser_opts_type = None @@ -291,14 +291,17 @@ def apply_log_masking(self, masker: MaskingFilter) -> MaskingFilter: @classmethod def from_raw_config( - cls, raw: RawConfig, repo: Repo, global_cli_options: GlobalCommandLineOptions + cls, raw: RawConfig, global_cli_options: GlobalCommandLineOptions ) -> RuntimeContext: ## # credentials masking for logging masker = MaskingFilter(_use_named_masks=raw.logging_use_named_masks) try: + repo = Repo(".", search_parent_directories=True) active_branch = repo.active_branch.name + except InvalidGitRepositoryError as err: + raise InvalidGitRepositoryError("No valid git repository found!") from err except TypeError as err: raise NotAReleaseBranch( "Detached HEAD state cannot match any release groups; " From e083056892086fdc70beb7a42d952e7c9604e995 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 9 Mar 2024 20:03:45 -0500 Subject: [PATCH 156/167] test: update references in test cases --- tests/command_line/conftest.py | 18 +++++++++++++----- tests/unit/semantic_release/cli/test_config.py | 11 +++++------ tests/util.py | 6 +++--- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/tests/command_line/conftest.py b/tests/command_line/conftest.py index 65b8c6b57..3ac92e6e0 100644 --- a/tests/command_line/conftest.py +++ b/tests/command_line/conftest.py @@ -1,5 +1,7 @@ from __future__ import annotations +import os +from pathlib import Path from typing import TYPE_CHECKING from unittest.mock import MagicMock @@ -7,7 +9,7 @@ from click.testing import CliRunner from requests_mock import ANY -from semantic_release.cli.commands import main +from semantic_release.cli import config as CliConfigModule from semantic_release.cli.config import ( GlobalCommandLineOptions, RawConfig, @@ -19,7 +21,6 @@ from tests.util import prepare_mocked_git_command_wrapper_type if TYPE_CHECKING: - from pathlib import Path from typing import Protocol from git.repo import Repo @@ -56,7 +57,7 @@ def mocked_git_push(monkeypatch: MonkeyPatch) -> MagicMock: """Mock the `Repo.git.push()` method in `semantic_release.cli.main`.""" mocked_push = MagicMock() cls = prepare_mocked_git_command_wrapper_type(push=mocked_push) - monkeypatch.setattr(main.Repo, "GitCommandWrapperType", cls) + monkeypatch.setattr(CliConfigModule.Repo, "GitCommandWrapperType", cls) return mocked_push @@ -90,7 +91,14 @@ def retrieve_runtime_context( cli_options: GlobalCommandLineOptions, ) -> RetrieveRuntimeContextFn: def _retrieve_runtime_context(repo: Repo) -> RuntimeContext: - raw_config = read_config_file(cli_options.config_file) - return RuntimeContext.from_raw_config(raw_config, repo, cli_options) + cwd = os.getcwd() + repo_dir = str(Path(repo.working_dir).resolve()) + + os.chdir(repo_dir) + try: + raw_config = read_config_file(cli_options.config_file) + return RuntimeContext.from_raw_config(raw_config, cli_options) + finally: + os.chdir(cwd) return _retrieve_runtime_context diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index 72447c6d5..1745bddd0 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -21,14 +21,13 @@ from semantic_release.const import DEFAULT_COMMIT_AUTHOR from semantic_release.enums import LevelBump +from tests.fixtures.repos import repo_with_no_tags_angular_commits from tests.util import CustomParserOpts if TYPE_CHECKING: from pathlib import Path from typing import Any - from git import Repo - from tests.fixtures.example_project import ExProjectDir @@ -144,11 +143,12 @@ def test_default_toml_config_valid(example_project_dir: ExProjectDir): ({"GIT_COMMIT_AUTHOR": "foo "}, "foo "), ], ) +@pytest.mark.usefixtures(repo_with_no_tags_angular_commits.__name__) def test_commit_author_configurable( example_pyproject_toml: Path, - repo_with_no_tags_angular_commits: Repo, mock_env: dict[str, str], expected_author: str, + change_to_ex_proj_dir: str, ): content = tomlkit.loads(example_pyproject_toml.read_text(encoding="utf-8")).unwrap() @@ -156,10 +156,9 @@ def test_commit_author_configurable( raw = RawConfig.model_validate(content) runtime = RuntimeContext.from_raw_config( raw=raw, - repo=repo_with_no_tags_angular_commits, global_cli_options=GlobalCommandLineOptions(), ) - assert ( + resulting_author = ( f"{runtime.commit_author.name} <{runtime.commit_author.email}>" - == expected_author ) + assert expected_author == resulting_author diff --git a/tests/util.py b/tests/util.py index 8c2b7d075..632ad2f4a 100644 --- a/tests/util.py +++ b/tests/util.py @@ -13,7 +13,7 @@ from semantic_release.changelog.context import make_changelog_context from semantic_release.changelog.release_history import ReleaseHistory -from semantic_release.cli.commands import main +from semantic_release.cli import config as cliConfigModule from semantic_release.commit_parser._base import CommitParser, ParserOptions from semantic_release.commit_parser.token import ParseResult @@ -36,7 +36,7 @@ _R = TypeVar("_R") - GitCommandWrapperType: TypeAlias = main.Repo.GitCommandWrapperType + GitCommandWrapperType: TypeAlias = cliConfigModule.Repo.GitCommandWrapperType def copy_dir_tree(src_dir: Path | str, dst_dir: Path | str) -> None: @@ -171,7 +171,7 @@ def prepare_mocked_git_command_wrapper_type( >>> mocked_push.assert_called_once() """ - class MockGitCommandWrapperType(main.Repo.GitCommandWrapperType): + class MockGitCommandWrapperType(cliConfigModule.Repo.GitCommandWrapperType): def __getattr__(self, name: str) -> Any: try: return object.__getattribute__(self, f"mocked_{name}") From e361f3455fa9b747617ed0270c54eb82d2cc1ac0 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Tue, 19 Mar 2024 05:12:59 +0000 Subject: [PATCH 157/167] 9.2.2 Automatically generated by python-semantic-release --- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf9fb2c62..682f36a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ +## v9.2.2 (2024-03-19) + +### Fix + +* fix(cli): enable subcommand help even if config is invalid + +Refactors configuration loading to use lazy loading by subcommands +triggered by the property access of the runtime_ctx object. Resolves +the issues when running `--help` on subcommands when a configuration +is invalid + +Resolves: #840 ([`91d221a`](https://github.com/python-semantic-release/python-semantic-release/commit/91d221a01266e5ca6de5c73296b0a90987847494)) + +### Test + +* test: update references in test cases ([`e083056`](https://github.com/python-semantic-release/python-semantic-release/commit/e083056892086fdc70beb7a42d952e7c9604e995)) + +* test(cli-help): add test cases of --help failures ([`1d53879`](https://github.com/python-semantic-release/python-semantic-release/commit/1d538790a727852d61c5edfe91c5707ed3b6af5a)) + + ## v9.2.1 (2024-03-19) ### Chore diff --git a/pyproject.toml b/pyproject.toml index b03656c70..13c8ca45a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.2.1" +version = "9.2.2" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 7842e7350..16865fd2b 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.2.1" +__version__ = "9.2.2" def setup_hook(argv: list[str]) -> None: From 37fdb28e0eb886d682b5dea4cc83a7c98a099422 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 21 Mar 2024 00:15:25 -0400 Subject: [PATCH 158/167] feat(cmd-version): changelog available to bundle (#779) * test(util): fix overlooked file differences in folder comparison * test(version): tracked changelog as changed file on version create Removes the temporary release_notes hack to prevent CHANGELOG generation on execution of version command. Now that it is implemented we can remove the fixture to properly pass the tests. * feat(cmd-version): create changelog prior to build enabling doc bundling --- semantic_release/cli/commands/version.py | 156 +++++++++++------------ tests/command_line/test_version.py | 8 +- tests/util.py | 2 +- 3 files changed, 77 insertions(+), 89 deletions(-) diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index f43983c15..c32f2a5bb 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -375,13 +375,71 @@ def version( # noqa: C901 rprint(f"[bold green]The next version is: [white]{new_version!s}[/white]! :rocket:") + rh = ReleaseHistory.from_git_history( + repo=repo, + translator=translator, + commit_parser=parser, + exclude_commit_patterns=changelog_excluded_commit_patterns, + ) + + commit_date = datetime.now() + try: + rh = rh.release( + new_version, + tagger=commit_author, + committer=commit_author, + tagged_date=commit_date, + ) + except ValueError as ve: + ctx.fail(str(ve)) + + all_paths_to_add: list[str] = [] + + if update_changelog: + changelog_context = make_changelog_context( + hvcs_client=hvcs_client, release_history=rh + ) + changelog_context.bind_to_environment(env) + + if template_dir.is_dir(): + if opts.noop: + noop_report( + f"would have recursively rendered the template directory " + f"{template_dir!r} relative to {repo.working_dir!r}. " + "Paths which would be modified by this operation cannot be " + "determined in no-op mode." + ) + else: + all_paths_to_add.extend( + recursive_render( + template_dir, environment=env, _root_dir=repo.working_dir + ) + ) + + else: + log.info( + "Path %r not found, using default changelog template", template_dir + ) + if opts.noop: + noop_report( + "would have written your changelog to " + + str(changelog_file.relative_to(repo.working_dir)) + ) + else: + changelog_text = render_default_changelog_file(env) + changelog_file.write_text(f"{changelog_text}\n", encoding="utf-8") + + all_paths_to_add.append(str(changelog_file.relative_to(repo.working_dir))) + + # Apply the new version to the source files files_with_new_version_written = apply_version_to_source_files( repo=repo, version_declarations=runtime.version_declarations, version=new_version, noop=opts.noop, ) - all_paths_to_add = files_with_new_version_written + (assets or []) + all_paths_to_add.extend(files_with_new_version_written) + all_paths_to_add.extend(assets or []) # Build distributions before committing any changes - this way if the # build fails, modifications to the source code won't be committed @@ -404,19 +462,11 @@ def version( # noqa: C901 # Commit changes if commit_changes and opts.noop: - # Indents the newlines so that terminal formatting is happy - note the - # git commit line of the output is 24 spaces indented too - # Only this message needs such special handling because of the newlines - # that might be in a commit message between the subject and body - indented_commit_message = commit_message.format(version=new_version).replace( - "\n\n", "\n\n" + " " * 24 - ) noop_report( indented( f""" would have run: git add {" ".join(all_paths_to_add)} - git commit -m "{indented_commit_message}" """ ) ) @@ -432,80 +482,6 @@ def version( # noqa: C901 except GitCommandError: # noqa: PERF203 log.warning("Failed to add path (%s) to index", updated_path) - rh = ReleaseHistory.from_git_history( - repo=repo, - translator=translator, - commit_parser=parser, - exclude_commit_patterns=changelog_excluded_commit_patterns, - ) - - commit_date = datetime.now() - try: - rh = rh.release( - new_version, - tagger=commit_author, - committer=commit_author, - tagged_date=commit_date, - ) - except ValueError as ve: - ctx.fail(str(ve)) - - changelog_context = make_changelog_context( - hvcs_client=hvcs_client, release_history=rh - ) - changelog_context.bind_to_environment(env) - - updated_paths: list[str] = [] - if update_changelog: - if template_dir.is_dir(): - if opts.noop: - noop_report( - f"would have recursively rendered the template directory " - f"{template_dir!r} relative to {repo.working_dir!r}. " - "Paths which would be modified by this operation cannot be " - "determined in no-op mode." - ) - else: - updated_paths = recursive_render( - template_dir, environment=env, _root_dir=repo.working_dir - ) - - else: - log.info( - "Path %r not found, using default changelog template", template_dir - ) - if opts.noop: - noop_report( - "would have written your changelog to " - + str(changelog_file.relative_to(repo.working_dir)) - ) - else: - changelog_text = render_default_changelog_file(env) - changelog_file.write_text(f"{changelog_text}\n", encoding="utf-8") - - updated_paths = [str(changelog_file.relative_to(repo.working_dir))] - - if commit_changes and opts.noop: - noop_report( - indented( - f""" - would have run: - git add {" ".join(updated_paths)} - """ - ) - ) - elif commit_changes: - # Anything changed here should be staged. - # TODO: in future this loop should be 1 line: - # repo.index.add(updated_paths, force=False) # noqa: ERA001 - # but since 'force' is deliberally ineffective (as in docstring) in gitpython 3.1.18 - # we have to do manually add each filepath, and catch the exception if it is an ignored file - for updated_path in updated_paths: - try: - repo.git.add(updated_path) - except GitCommandError: # noqa: PERF203 - log.warning("Failed to add path (%s) to index", updated_path) - def custom_git_environment() -> ContextManager[None]: """ git.custom_environment is a context manager but @@ -541,7 +517,16 @@ def custom_git_environment() -> ContextManager[None]: if commit_author else "" ) - command += "git commit -m '{commit_message.format(version=v)}'" + + # Indents the newlines so that terminal formatting is happy - note the + # git commit line of the output is 24 spaces indented too + # Only this message needs such special handling because of the newlines + # that might be in a commit message between the subject and body + indented_commit_message = commit_message.format(version=new_version).replace( + "\n\n", "\n\n" + " " * 24 + ) + + command += f"git commit -m '{indented_commit_message}'" noop_report( indented( @@ -619,6 +604,9 @@ def custom_git_environment() -> ContextManager[None]: # Use a new, non-configurable environment for release notes - # not user-configurable at the moment release_note_environment = environment(template_dir=runtime.template_dir) + changelog_context = make_changelog_context( + hvcs_client=hvcs_client, release_history=rh + ) changelog_context.bind_to_environment(release_note_environment) template = get_release_notes_template(template_dir) diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 8e2da4eec..fc648128d 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -672,18 +672,19 @@ def test_version_tag_only_push( def test_version_only_update_files_no_git_actions( mocked_git_push: MagicMock, repo_with_single_branch_and_prereleases_angular_commits: Repo, - use_release_notes_template: UseReleaseNotesTemplateFn, retrieve_runtime_context: RetrieveRuntimeContextFn, cli_runner: CliRunner, tmp_path_factory: pytest.TempPathFactory, example_pyproject_toml: Path, example_project_dir: ExProjectDir, + example_changelog_md: Path, ) -> None: # Setup - use_release_notes_template() runtime_context_with_tags = retrieve_runtime_context( repo_with_single_branch_and_prereleases_angular_commits ) + # Remove the previously created changelog to allow for it to be generated + example_changelog_md.unlink() # Arrange expected_new_version = "0.3.0" @@ -718,8 +719,7 @@ def test_version_only_update_files_no_git_actions( # Files that should receive version change expected_changed_files = sorted( [ - # CHANGELOG.md is not included as no modification to Git History - # (no commit or tag) has been made + "CHANGELOG.md", "pyproject.toml", f"src/{EXAMPLE_PROJECT_NAME}/_version.py", ] diff --git a/tests/util.py b/tests/util.py index 632ad2f4a..2f8f9ae7f 100644 --- a/tests/util.py +++ b/tests/util.py @@ -106,7 +106,7 @@ def netrc_file(machine: str) -> NamedTemporaryFile: def flatten_dircmp(dcmp: filecmp.dircmp) -> list[str]: - return dcmp.diff_files + [ + return dcmp.diff_files + dcmp.left_only + dcmp.right_only + [ os.sep.join((directory, file)) for directory, cmp in dcmp.subdirs.items() for file in flatten_dircmp(cmp) From bc841cd171c46e1e23681f0f02d8f314281857c4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 21 Mar 2024 04:21:04 +0000 Subject: [PATCH 159/167] style: beautify 37fdb28e0eb886d682b5dea4cc83a7c98a099422 --- tests/util.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/util.py b/tests/util.py index 2f8f9ae7f..2d8774f56 100644 --- a/tests/util.py +++ b/tests/util.py @@ -106,11 +106,16 @@ def netrc_file(machine: str) -> NamedTemporaryFile: def flatten_dircmp(dcmp: filecmp.dircmp) -> list[str]: - return dcmp.diff_files + dcmp.left_only + dcmp.right_only + [ - os.sep.join((directory, file)) - for directory, cmp in dcmp.subdirs.items() - for file in flatten_dircmp(cmp) - ] + return ( + dcmp.diff_files + + dcmp.left_only + + dcmp.right_only + + [ + os.sep.join((directory, file)) + for directory, cmp in dcmp.subdirs.items() + for file in flatten_dircmp(cmp) + ] + ) def xdist_sort_hack(it: Iterable[_R]) -> Iterable[_R]: From 7595f9eda6d154d0bf9df22b3b7f18ef0b29d537 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Thu, 21 Mar 2024 04:23:16 +0000 Subject: [PATCH 160/167] 9.3.0 Automatically generated by python-semantic-release --- CHANGELOG.md | 21 +++++++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 682f36a80..ae5d2d14e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ +## v9.3.0 (2024-03-21) + +### Feature + +* feat(cmd-version): changelog available to bundle (#779) + +* test(util): fix overlooked file differences in folder comparison + +* test(version): tracked changelog as changed file on version create + +Removes the temporary release_notes hack to prevent CHANGELOG generation on +execution of version command. Now that it is implemented we can remove the +fixture to properly pass the tests. + +* feat(cmd-version): create changelog prior to build enabling doc bundling ([`37fdb28`](https://github.com/python-semantic-release/python-semantic-release/commit/37fdb28e0eb886d682b5dea4cc83a7c98a099422)) + +### Style + +* style: beautify 37fdb28e0eb886d682b5dea4cc83a7c98a099422 ([`bc841cd`](https://github.com/python-semantic-release/python-semantic-release/commit/bc841cd171c46e1e23681f0f02d8f314281857c4)) + + ## v9.2.2 (2024-03-19) ### Fix diff --git a/pyproject.toml b/pyproject.toml index 13c8ca45a..a3360c168 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.2.2" +version = "9.3.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index 16865fd2b..fc245e530 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.2.2" +__version__ = "9.3.0" def setup_hook(argv: list[str]) -> None: From 8a6adeb6824a0b78e3dd297c1e62e5453a9f779f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 00:51:35 -0400 Subject: [PATCH 161/167] build(deps-dev): bump ruff from 0.1.11 to 0.3.3 (#862) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a3360c168..c6caa49df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ test = [ "requests-mock>=1.10.0,<2", "types-pytest-lazy-fixture>=0.6.3.3", ] -dev = ["pre-commit", "tox", "ruff==0.3.2"] +dev = ["pre-commit", "tox", "ruff==0.3.3"] mypy = ["mypy", "types-requests"] [tool.pytest.ini_options] From 3690b9511de633ab38083de4d2505b6d05853346 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 23 Mar 2024 11:04:24 -0400 Subject: [PATCH 162/167] perf(algorithm): simplify logs & use lookup when searching for commit & tag match --- semantic_release/version/algorithm.py | 77 +++++++++++++-------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/semantic_release/version/algorithm.py b/semantic_release/version/algorithm.py index 41c45505e..acb71c402 100644 --- a/semantic_release/version/algorithm.py +++ b/semantic_release/version/algorithm.py @@ -4,9 +4,7 @@ from queue import Queue from typing import TYPE_CHECKING, Iterable -from semantic_release.commit_parser import ( - ParsedCommit, -) +from semantic_release.commit_parser import ParsedCommit from semantic_release.const import DEFAULT_VERSION from semantic_release.enums import LevelBump from semantic_release.errors import InvalidVersion @@ -278,9 +276,9 @@ def next_version( """ # Step 1. All tags, sorted descending by semver ordering rules all_git_tags_as_versions = tags_and_versions(repo.tags, translator) - all_full_release_tags_and_versions = [ - (t, v) for t, v in all_git_tags_as_versions if not v.is_prerelease - ] + all_full_release_tags_and_versions = list(filter( + lambda t_v: not t_v[1].is_prerelease, all_git_tags_as_versions + )) log.info( "Found %s full releases (excluding prereleases)", len(all_full_release_tags_and_versions), @@ -350,6 +348,23 @@ def next_version( tag_format=translator.tag_format, ) + # We only include pre-releases here if doing a prerelease. + # If it's not a prerelease, we need to include commits back + # to the last full version in consideration for what kind of + # bump to produce. However if we're doing a prerelease, we can + # include prereleases here to potentially consider a smaller portion + # of history (from a prerelease since the last full release, onwards) + + # Note that a side-effect of this is, if at some point the configuration + # for a particular branch pattern changes w.r.t. prerelease=True/False, + # the new kind of version will be produced from the commits already + # included in a prerelease since the last full release on the branch + tag_sha_2_version_lookup = { + tag.commit.hexsha: (tag, version) + for tag, version in all_git_tags_as_versions + if prerelease or not version.is_prerelease + } + # N.B. these should be sorted so long as we iterate the commits in reverse order for commit in commits_since_last_full_release: parse_result = commit_parser.parse(commit) @@ -360,44 +375,24 @@ def next_version( ) parsed_levels.add(parse_result.bump) - # We only include pre-releases here if doing a prerelease. - # If it's not a prerelease, we need to include commits back - # to the last full version in consideration for what kind of - # bump to produce. However if we're doing a prerelease, we can - # include prereleases here to potentially consider a smaller portion - # of history (from a prerelease since the last full release, onwards) - - # Note that a side-effect of this is, if at some point the configuration - # for a particular branch pattern changes w.r.t. prerelease=True/False, - # the new kind of version will be produced from the commits already - # included in a prerelease since the last full release on the branch - for tag, version in ( - (tag, version) - for tag, version in all_git_tags_as_versions - if prerelease or not version.is_prerelease - ): - log.debug( - "testing if tag %r (%s) matches commit %s", - tag.name, - tag.commit.hexsha, - commit.hexsha, - ) - if tag.commit == commit: - latest_version = version - log.debug( - "tag %r (%s) matches commit %s. the latest version is %s", - tag.name, - tag.commit.hexsha, - commit.hexsha, - latest_version, - ) - break - else: + log.debug("checking if commit %s matches any tags", commit.hexsha) + t_v = tag_sha_2_version_lookup.get(commit.hexsha, None) + + if t_v is None: # If we haven't found the latest prerelease on the branch, - # keep the outer loop going to look for it + # keep the loop going to look for it log.debug("no tags correspond to commit %s", commit.hexsha) continue - # If we found it in the inner loop, break the outer loop too + + # Unpack the tuple + tag, latest_version = t_v + log.debug( + "tag %r (%s) matches commit %s. the latest version is %s", + tag.name, + tag.commit.hexsha, + commit.hexsha, + latest_version, + ) break log.debug( From 4c998b77a3fe5e12783d1ab2d47789a10b83f247 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 23 Mar 2024 11:30:46 -0400 Subject: [PATCH 163/167] fix(algorithm): handle merge-base errors gracefully Merge-base errors generally occur from a shallow clone that is primarily used by CI environments and will cause PSR to explode prior to this change. Now it exits with an appropriate error. Resolves: #724 --- semantic_release/errors.py | 7 ++++ semantic_release/version/algorithm.py | 60 ++++++++++++++++++--------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/semantic_release/errors.py b/semantic_release/errors.py index 7a02a17c5..36712ab55 100644 --- a/semantic_release/errors.py +++ b/semantic_release/errors.py @@ -31,3 +31,10 @@ class CommitParseError(SemanticReleaseBaseError): Raised when a commit cannot be parsed by a commit parser. Custom commit parsers should also raise this Exception """ + + +class MissingMergeBaseError(SemanticReleaseBaseError): + """ + Raised when the merge base cannot be found with the current history. Generally + because of a shallow git clone. + """ diff --git a/semantic_release/version/algorithm.py b/semantic_release/version/algorithm.py index acb71c402..99990cd22 100644 --- a/semantic_release/version/algorithm.py +++ b/semantic_release/version/algorithm.py @@ -7,7 +7,7 @@ from semantic_release.commit_parser import ParsedCommit from semantic_release.const import DEFAULT_VERSION from semantic_release.enums import LevelBump -from semantic_release.errors import InvalidVersion +from semantic_release.errors import InvalidVersion, MissingMergeBaseError from semantic_release.version.version import Version if TYPE_CHECKING: @@ -289,30 +289,50 @@ def next_version( iter(all_full_release_tags_and_versions), (None, translator.from_string(DEFAULT_VERSION)), ) - if latest_full_release_tag is None: - # Workaround - we can safely scan the extra commits on this - # branch if it's never been released, but we have no other - # guarantees that other branches exist - log.info( - "No full releases have been made yet, the default version to use is %s", - latest_full_release_version, - ) - merge_bases = repo.merge_base(repo.active_branch, repo.active_branch) - else: - # Note the merge_base might be on our current branch, it's not - # necessarily the merge base of the current branch with `main` - log.info( - "The last full release was %s, tagged as %r", - latest_full_release_version, - latest_full_release_tag, + + # we can safely scan the extra commits on this + # branch if it's never been released, but we have no other + # guarantees that other branches exist + # Note the merge_base might be on our current branch, it's not + # necessarily the merge base of the current branch with `main` + other_ref = ( + repo.active_branch + if latest_full_release_tag is None + else latest_full_release_tag.name + ) + + # Conditional log message to inform what was chosen as the comparison point + # to find the merge base of the current branch with the latest full release + log_msg = ( + str.join(", ", [ + "No full releases have been made yet", + f"the default version to use is {latest_full_release_version}", + ]) + if latest_full_release_tag is None + else str.join(", ", [ + f"The last full release was {latest_full_release_version}", + f"tagged as {latest_full_release_tag!r}", + ]) + ) + + log.info(log_msg) + merge_bases = repo.merge_base(other_ref, repo.active_branch) + + if len(merge_bases) < 1: + raise MissingMergeBaseError( + f"Unable to find merge-base between {other_ref} and {repo.active_branch.name}" ) - merge_bases = repo.merge_base(latest_full_release_tag.name, repo.active_branch) + if len(merge_bases) > 1: raise NotImplementedError( - "This branch has more than one merge-base with the " - "latest version, which is not yet supported" + str.join(" ", [ + "This branch has more than one merge-base with the", + "latest version, which is not yet supported" + ]) ) + merge_base = merge_bases[0] + if merge_base is None: str_tag_name = ( "None" if latest_full_release_tag is None else latest_full_release_tag.name From 8a9da4feb8753e3ab9ea752afa25decd2047675a Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 23 Mar 2024 12:16:48 -0400 Subject: [PATCH 164/167] fix(cli-version): change implementation to only push the tag we generated Restricts the git push command to only push the explicit tag we created which will eliminate the possibility of pushing another tag that could cause an error. Resolves: #803 --- semantic_release/cli/commands/version.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index c32f2a5bb..2fbaad790 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -585,12 +585,17 @@ def custom_git_environment() -> ContextManager[None]: indented( f""" would have run: - git push --tags {runtime.masker.mask(remote_url)} {active_branch} + git push {runtime.masker.mask(remote_url)} tag {new_version.as_tag()} """ # noqa: E501 ) ) elif create_tag: - repo.git.push("--tags", remote_url, active_branch) + # push specific tag refspec (that we made) to remote + # --------------- + # Resolves issue #803 where a tag that already existed was pushed and caused + # a failure. Its not clear why there was an incorrect tag (likely user error change) + # but we will avoid possibly pushing an separate tag that we didn't create. + repo.git.push(remote_url, "tag", new_version.as_tag()) gha_output.released = True From 3bf98d5c18886f9dcc60d122a834e6fe93d0ea07 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 24 Mar 2024 04:08:39 +0000 Subject: [PATCH 165/167] style: beautify 8a9da4feb8753e3ab9ea752afa25decd2047675a --- semantic_release/version/algorithm.py | 39 ++++++++++++++++----------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/semantic_release/version/algorithm.py b/semantic_release/version/algorithm.py index 99990cd22..47d6caee5 100644 --- a/semantic_release/version/algorithm.py +++ b/semantic_release/version/algorithm.py @@ -276,9 +276,9 @@ def next_version( """ # Step 1. All tags, sorted descending by semver ordering rules all_git_tags_as_versions = tags_and_versions(repo.tags, translator) - all_full_release_tags_and_versions = list(filter( - lambda t_v: not t_v[1].is_prerelease, all_git_tags_as_versions - )) + all_full_release_tags_and_versions = list( + filter(lambda t_v: not t_v[1].is_prerelease, all_git_tags_as_versions) + ) log.info( "Found %s full releases (excluding prereleases)", len(all_full_release_tags_and_versions), @@ -304,15 +304,21 @@ def next_version( # Conditional log message to inform what was chosen as the comparison point # to find the merge base of the current branch with the latest full release log_msg = ( - str.join(", ", [ - "No full releases have been made yet", - f"the default version to use is {latest_full_release_version}", - ]) + str.join( + ", ", + [ + "No full releases have been made yet", + f"the default version to use is {latest_full_release_version}", + ], + ) if latest_full_release_tag is None - else str.join(", ", [ - f"The last full release was {latest_full_release_version}", - f"tagged as {latest_full_release_tag!r}", - ]) + else str.join( + ", ", + [ + f"The last full release was {latest_full_release_version}", + f"tagged as {latest_full_release_tag!r}", + ], + ) ) log.info(log_msg) @@ -325,10 +331,13 @@ def next_version( if len(merge_bases) > 1: raise NotImplementedError( - str.join(" ", [ - "This branch has more than one merge-base with the", - "latest version, which is not yet supported" - ]) + str.join( + " ", + [ + "This branch has more than one merge-base with the", + "latest version, which is not yet supported", + ], + ) ) merge_base = merge_bases[0] From 12517edf299b8f04a1e3f12dda85c383ceb864c0 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sun, 24 Mar 2024 04:10:40 +0000 Subject: [PATCH 166/167] 9.3.1 Automatically generated by python-semantic-release --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- semantic_release/__init__.py | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae5d2d14e..fb8217120 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ +## v9.3.1 (2024-03-24) + +### Build + +* build(deps-dev): bump ruff from 0.1.11 to 0.3.3 (#862) ([`8a6adeb`](https://github.com/python-semantic-release/python-semantic-release/commit/8a6adeb6824a0b78e3dd297c1e62e5453a9f779f)) + +### Fix + +* fix(cli-version): change implementation to only push the tag we generated + +Restricts the git push command to only push the explicit tag we created +which will eliminate the possibility of pushing another tag that could +cause an error. + +Resolves: #803 ([`8a9da4f`](https://github.com/python-semantic-release/python-semantic-release/commit/8a9da4feb8753e3ab9ea752afa25decd2047675a)) + +* fix(algorithm): handle merge-base errors gracefully + +Merge-base errors generally occur from a shallow clone that is +primarily used by CI environments and will cause PSR to explode +prior to this change. Now it exits with an appropriate error. + +Resolves: #724 ([`4c998b7`](https://github.com/python-semantic-release/python-semantic-release/commit/4c998b77a3fe5e12783d1ab2d47789a10b83f247)) + +### Performance + +* perf(algorithm): simplify logs & use lookup when searching for commit & tag match ([`3690b95`](https://github.com/python-semantic-release/python-semantic-release/commit/3690b9511de633ab38083de4d2505b6d05853346)) + +### Style + +* style: beautify 8a9da4feb8753e3ab9ea752afa25decd2047675a ([`3bf98d5`](https://github.com/python-semantic-release/python-semantic-release/commit/3bf98d5c18886f9dcc60d122a834e6fe93d0ea07)) + + ## v9.3.0 (2024-03-21) ### Feature diff --git a/pyproject.toml b/pyproject.toml index c6caa49df..c562bc92c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "9.3.0" +version = "9.3.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } diff --git a/semantic_release/__init__.py b/semantic_release/__init__.py index fc245e530..29ae9f614 100644 --- a/semantic_release/__init__.py +++ b/semantic_release/__init__.py @@ -24,7 +24,7 @@ tags_and_versions, ) -__version__ = "9.3.0" +__version__ = "9.3.1" def setup_hook(argv: list[str]) -> None: From a352050d506947db6890f5cc0d964e4a3850949d Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 24 Mar 2024 12:05:33 -0400 Subject: [PATCH 167/167] chore(stalebot): refactor to use CI action for stale issue/pr evaluation --- .github/dependabot.yaml | 2 ++ .github/stale.yml | 58 ------------------------------------- .github/workflows/stale.yml | 45 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 58 deletions(-) delete mode 100644 .github/stale.yml create mode 100644 .github/workflows/stale.yml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 3161cb209..bb0921032 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -13,6 +13,7 @@ updates: include: "scope" labels: - dependencies + - dependabot open-pull-requests-limit: 10 rebase-strategy: auto versioning-strategy: "increase-if-necessary" @@ -27,6 +28,7 @@ updates: prefix: "ci" labels: - dependencies + - dependabot rebase-strategy: auto groups: github-actions: diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 68c4b43eb..000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,58 +0,0 @@ -# Configuration for probot-stale # -# CAO: https://github.com/probot/stale/tree/4232daafc73492864b559b55c9cf2689d19d5b23 # -# ================================================================================== # -# Rule summary -# ---------------------------------------------------------------------------------- # -# Comment & mark 90-day old issues as stale -# Close stalled issues after 7 days -# Comment & mark 60-day old PR as stale -# Close stalled PRs after 10 days -# Any issue/pr with a planned milestone is exempt -# Issues/PRs with a confirmed label are exempt -# ================================================================================== # -# See repo url for descriptions of settings - -# ALL -staleLabel: stale -limitPerRun: 30 # numActions/hour - - -issues: - daysUntilStale: 90 - daysUntilClose: 7 - exemptProjects: false - exemptMilestones: true - exemptAssignees: false - exemptLabels: - - confirmed - - help-wanted - markComment: > - This issue is stale because it has been open 90 days with no recent activity. - It will be closed in 7 days, if no further activity occurs. Thank you for - your contributions. - unmarkComment: > - Thank you for the update and contribution! The stale label has been removed - and this issue will not be closed at this time. - closeComment: > - This issue was closed because activity was dormant for 97 days. - - -pulls: - daysUntilStale: 60 - daysUntilClose: 10 - exemptProjects: false - exemptMilestones: true - exemptAssignees: false - exemptLabels: - - confirmed - # Ignore PRs opened by Dependabot - - dependencies - markComment: > - This PR is stale because it has been open 60 days with no recent activity. - It will be closed in 10 days, if no further activity occurs. Thank you for - your contributions. - unmarkComment: > - Thank you for the update and contribution! The stale label has been removed - and this PR will not be closed at this time. - closeComment: > - This PR was closed because activity was dormant for 70 days. diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..207a6c61d --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,45 @@ +name: 'Stale Bot' +on: + schedule: + # Execute Daily at 7:15 AM UTC + - cron: '15 7 * * *' + +# Default token permissions = None +permissions: {} + + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + pull-requests: write + steps: + - uses: actions/stale@v9 + with: + # default: 30, GitHub Actions API Rate limit is 1000/hr + operations-per-run: 200 + exempt-all-milestones: true + # exempt-all-assignees: false (default) + stale-issue-label: stale + days-before-issue-stale: 90 + days-before-issue-close: 7 + exempt-issue-labels: confirmed, help-wanted + stale-issue-message: > + This issue is stale because it has not been confirmed or planned by the maintainers + and has been open 90 days with no recent activity. It will be closed in 7 days, + if no further activity occurs. Thank you for your contributions. + close-issue-message: > + This issue was closed because activity was dormant for 97 days. + # PR Configurations + stale-pr-label: stale + days-before-pr-stale: 60 + days-before-pr-close: 10 + exempt-pr-labels: confirmed, dependabot + stale-pr-message: > + This PR is stale because it has not been confirmed or planned by the maintainers + and had been open 60 days with no recent activity. It will be closed in 10 days, + if no further activity occurs. Thank you for your contributions. + close-pr-message: > + This PR was closed because activity was dormant for 70 days.