From 7da6fd667f3a262c9d4be7f142fb4cab7c48600d Mon Sep 17 00:00:00 2001 From: SafetyQuincyF Date: Wed, 23 Oct 2024 14:14:07 -0400 Subject: [PATCH] feat: add changelog generation and version update --- CHANGELOG.md | 11 ++ safety/VERSION | 2 +- scripts/release_scripts/increment_version.py | 147 +++++++++++++++++++ tests/test_cli.py | 4 +- 4 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 scripts/release_scripts/increment_version.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 687ad97f..7f23d5e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is partly based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and [PEP 440](https://peps.python.org/pep-0440/) +## [3.2.9] - 2024-10-23 +- chore: deprection-message-for-license-command (4149b70) +- feat: add-pull-request-template (#604) (61b2fe2) +- fix: devcontainer fix (be42d8e) +- fix: safety error when scan is run without being authed (5ec80dd) +- feat: add-devcontainers-support (0591838) +- fix: internal-server-error (04d7efb) +- fix: clarify-vulnerabilities-found/ Fixed the issue where the vulnerabilities (07bc5b7) +- chore: added check arg depreciation warning (78109e5) +- feature: release-script: add release script (#602) (cc49542) + ## [3.2.8] - 2024-09-27 - feat: enhance version comparison logic for check-updates command (#605) - docs: add demo Jupyter Notebook (#601) diff --git a/safety/VERSION b/safety/VERSION index f092941a..df4bdc7e 100644 --- a/safety/VERSION +++ b/safety/VERSION @@ -1 +1 @@ -3.2.8 +3.2.9 \ No newline at end of file diff --git a/scripts/release_scripts/increment_version.py b/scripts/release_scripts/increment_version.py new file mode 100644 index 00000000..cd014712 --- /dev/null +++ b/scripts/release_scripts/increment_version.py @@ -0,0 +1,147 @@ +import subprocess +import re +from datetime import date +import sys + + +def get_current_version(file_path: str) -> str: + """Read the current version from the VERSION file.""" + with open(file_path, "r") as file: + return file.read().strip() + + +def get_last_version_from_changelog(file_path: str) -> str: + """Extract the last version noted in the CHANGELOG.MD file.""" + with open(file_path, "r") as file: + content = file.read() + match = re.search(r"\[(\d+\.\d+\.\d+)\] - \d{4}-\d{2}-\d{2}", content) + return match.group(1) if match else None + + +def increment_version(version: str, bump_type: str) -> str: + """Increment the version number based on the bump type.""" + major, minor, patch = map(int, version.split(".")) + + if bump_type == "major": + major += 1 + minor = 0 + patch = 0 + elif bump_type == "minor": + minor += 1 + patch = 0 + elif bump_type == "patch": + patch += 1 + else: + raise ValueError("Invalid bump type. Use 'major', 'minor', or 'patch'.") + + return f"{major}.{minor}.{patch}" + + +def update_version_file(file_path: str, new_version: str): + """Update the VERSION file with the new version.""" + with open(file_path, "w") as file: + file.write(new_version) + + +def get_git_commits_since_last_version(last_version: str) -> str: + """Get all git commits since the last version.""" + result = subprocess.run( + ["git", "log", f"{last_version}..HEAD", "--pretty=format:%s (%h)"], + capture_output=True, + text=True, + ) + if result.returncode != 0: + raise RuntimeError(f"Error running git log: {result.stderr}") + return result.stdout.strip() + + +def format_commit_message(commit_message: str) -> str: + """Format the commit message according to the changelog format.""" + if "Merge pull request" in commit_message: + return None + + # Replace only the first occurrence of '/' with a ':' + slash_index = commit_message.find("/") + if slash_index != -1: + commit_message = ( + commit_message[:slash_index] + ": " + commit_message[slash_index + 1 :] + ) + return f"- {commit_message}" + else: + return f"- fix: {commit_message}" + + +def update_changelog(file_path: str, new_version: str, new_commits: str): + """Add new version and commits to the changelog file in the correct section.""" + today = date.today().strftime("%Y-%m-%d") + formatted_commits = "\n".join( + formatted_commit + for commit in new_commits.split("\n") + if (formatted_commit := format_commit_message(commit)) + ) + + new_changelog_entry = f"## [{new_version}] - {today}\n{formatted_commits}\n" + + with open(file_path, "r+") as file: + content = file.read() + + if "[PEP 440](https://peps.python.org/pep-0440/)" in content: + content = content.replace( + "[PEP 440](https://peps.python.org/pep-0440/)", + "[PEP 440](https://peps.python.org/pep-0440/)\n", + ) + + changelog_header_index = content.find("# Changelog") + if changelog_header_index == -1: + raise Exception("Changelog file is missing the '# Changelog' header") + + insertion_point = content.find("\n## [", changelog_header_index) + if insertion_point == -1: + insertion_point = len(content) + + updated_content = ( + content[:insertion_point] + new_changelog_entry + content[insertion_point:] + ) + + file.seek(0) + file.write(updated_content) + file.truncate() + + +def main(): + if len(sys.argv) < 2: + print("Usage: python script.py ") + print("bump_type: major, minor, or patch") + return + + bump_type = sys.argv[1] + version_file = "safety/VERSION" + changelog_file = "CHANGELOG.md" + + # Get current version and last version in changelog + current_version = get_current_version(version_file) + last_version = get_last_version_from_changelog(changelog_file) + + if not last_version: + print("No previous version found in changelog.") + return + + # Increment the current version based on the bump type + new_version = increment_version(current_version, bump_type) + + # Update the version file with the new version + update_version_file(version_file, new_version) + + # Get git commits since the last version + commits = get_git_commits_since_last_version(last_version) + if not commits: + print("No new commits since the last version.") + return + + # Update the changelog with new version and commits + update_changelog(changelog_file, new_version, commits) + print(f"CHANGELOG.MD updated with version {new_version}") + + +if __name__ == "__main__": + main() diff --git a/tests/test_cli.py b/tests/test_cli.py index 14082564..1aad2183 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -18,7 +18,7 @@ from safety import cli from safety.models import CVE, SafetyRequirement, Severity, Vulnerability -from safety.util import Package, SafetyContext +from safety.util import Package, SafetyContext, get_safety_version from safety.auth.models import Auth from safety_schemas.models.base import AuthenticationType @@ -533,7 +533,7 @@ def test_debug_flag(self, mock_get_auth_info, mock_is_valid, mock_get_auth_type, assert result.exit_code == 0, ( f"CLI exited with code {result.exit_code} and output: {result.output} and error: {result.stderr}" ) - expected_output_snippet = "Safety 3.2.8 scanning" + expected_output_snippet = f"{get_safety_version()} scanning" assert expected_output_snippet in result.output, ( f"Expected output to contain: {expected_output_snippet}, but got: {result.output}" )