From 30477d4c17364e6809715203bc03217c477b7631 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Fri, 18 Sep 2020 10:59:14 +0200 Subject: [PATCH 1/3] separate gpg sign of zip and tarballs from actual release create signing of tar and zipballs into an own command 'sign' instead of enforcing it in release. --- CHANGELOG.md | 1 + pontos/release/release.py | 274 ++++++++++++++++++++-------------- tests/release/test_release.py | 126 +++++++++++++++- 3 files changed, 285 insertions(+), 116 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef399accf..7b8ec85b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - add handling of PROJECT_DEV_VERSION in CMakeLists.txt if set [#32](https://github.com/greenbone/pontos/pull/32) ### Changed - set releasename to projectname version [#25](https://github.com/greenbone/pontos/pull/25) +- separate signing tar and zipballs from release into a own command `sign` [#](https://github.com/greenbone/pontos/pull/xx) ### Deprecated ### Removed ### Fixed diff --git a/pontos/release/release.py b/pontos/release/release.py index 10aab7082..5a01131da 100644 --- a/pontos/release/release.py +++ b/pontos/release/release.py @@ -25,7 +25,8 @@ import shutil from pathlib import Path -from typing import Callable, Dict, List, Union, Tuple +from typing import Callable, Dict, List, Union, Tuple, Optional +from dataclasses import dataclass import requests @@ -33,6 +34,31 @@ from pontos import changelog +@dataclass +class ReleaseInformation: + release_version: str + nextversion: str + project: str + space: str + git_signing_key: str + git_tag_prefix: str + git_remote_name: str + username: str + token: str + signing_key: Optional[str] + path: Path + requests_module: requests + version_module: version + changelog_module: changelog + shell_cmd_runner: Callable + + def git_version(self): + return "{}{}".format(self.git_tag_prefix, self.release_version) + + def auth(self): + return (self.username, self.token) + + def build_release_dict( release_version: str, release_changelog: str, @@ -109,6 +135,7 @@ def initialize_default_parser() -> argparse.ArgumentParser: subparsers.add_parser('prepare') subparsers.add_parser('release') + subparsers.add_parser('sign') return parser @@ -171,39 +198,34 @@ def commit_files( def prepare( - release_version: str, - nextversion: str, - project: str, - space: str, - signing_key: str, - git_signing_key: str, - git_tag_prefix: str, - shell_cmd_runner: Callable, - _path: Path, - _requests: requests, - _version: version, - _changelog: changelog, + release_info: ReleaseInformation, ): print("in prepare") # guardian - git_tags = shell_cmd_runner('git tag -l') - git_version = "{}{}".format(git_tag_prefix, release_version) + git_tags = release_info.shell_cmd_runner('git tag -l') + git_version = "{}{}".format( + release_info.git_tag_prefix, release_info.release_version + ) if git_version.encode() in git_tags.stdout.splitlines(): raise ValueError('git tag {} is already taken.'.format(git_version)) executed, filename = update_version( - release_version, _version, develop=False + release_info.release_version, release_info.version_module, develop=False ) if not executed: return False - print("updated version {} to {}".format(filename, release_version)) + print( + "updated version {} to {}".format( + filename, release_info.release_version + ) + ) - change_log_path = _path.cwd() / 'CHANGELOG.md' - updated, changelog_text = _changelog.update( + change_log_path = release_info.path.cwd() / 'CHANGELOG.md' + updated, changelog_text = release_info.changelog_module.update( change_log_path.read_text(), - release_version, - git_tag_prefix=git_tag_prefix, + release_info.release_version, + git_tag_prefix=release_info.git_tag_prefix, ) if not updated: @@ -215,41 +237,53 @@ def prepare( print("Committing changes") - commit_msg = 'automatic release to {}'.format(release_version) - commit_files(filename, commit_msg, signing_key, shell_cmd_runner) + commit_msg = 'automatic release to {}'.format(release_info.release_version) + commit_files( + filename, + commit_msg, + release_info.signing_key, + release_info.shell_cmd_runner, + ) - if git_signing_key: - shell_cmd_runner( + if release_info.git_signing_key: + release_info.shell_cmd_runner( "git tag -u {} {} -m '{}'".format( - git_signing_key, git_version, commit_msg + release_info.git_signing_key, git_version, commit_msg ), ) else: - shell_cmd_runner( + release_info.shell_cmd_runner( "git tag -s {} -m '{}'".format(git_version, commit_msg), ) - release_text = _path(".release.txt.md") + release_text = release_info.path(".release.txt.md") release_text.write_text(changelog_text) # set to new version add skeleton - executed, filename = update_version(nextversion, _version, develop=True) + executed, filename = update_version( + release_info.nextversion, release_info.version_module, develop=True + ) if not executed: return False - updated = _changelog.add_skeleton( + updated = release_info.changelog_module.add_skeleton( change_log_path.read_text(), - release_version, - project, - git_tag_prefix=git_tag_prefix, - git_space=space, + release_info.release_version, + release_info.project, + git_tag_prefix=release_info.git_tag_prefix, + git_space=release_info.space, ) change_log_path.write_text(updated) commit_msg = 'set version {}, add empty changelog after {}'.format( - nextversion, release_version + release_info.nextversion, release_info.release_version + ) + commit_files( + filename, + commit_msg, + release_info.signing_key, + release_info.shell_cmd_runner, ) - commit_files(filename, commit_msg, signing_key, shell_cmd_runner) print( "Please verify git tag {}, commit and release text in {}".format( @@ -262,27 +296,53 @@ def prepare( def release( - release_version: str, - project: str, - space: str, - signing_key: str, - username: str, - token: str, - git_tag_prefix: str, - git_remote_name: str, - shell_cmd_runner: Callable, - _path: Path, - _requests: requests, - _version: version, - _changelog: changelog, + release_info: ReleaseInformation, ): - auth = (username, token) + changelog_text = release_info.path(".release.txt.md").read_text() + + print("Pushing changes") + + if release_info.git_remote_name: + release_info.shell_cmd_runner( + "git push --follow-tags {}".format(release_info.git_remote_name) + ) + else: + release_info.shell_cmd_runner("git push --follow-tags") + + print("Creating release") + + headers = {'Accept': 'application/vnd.github.v3+json'} + + base_url = "https://api.github.com/repos/{}/{}/releases".format( + release_info.space, release_info.project + ) + response = release_info.requests_module.post( + base_url, + headers=headers, + auth=release_info.auth(), + json=build_release_dict( + release_info.git_version(), + changelog_text, + name="{} {}".format( + release_info.project, release_info.release_version + ), + ), + ) + if response.status_code != 201: + print("Wrong response status code: {}".format(response.status_code)) + print(json.dumps(response.text, indent=4, sort_keys=True)) + return False + + release_info.path(".release.txt.md").unlink() + return True - def upload_assets(pathnames: List[str], github_json: str) -> bool: + +def sign(release_info: ReleaseInformation): + def upload_assets(pathnames: List[str], github_json: Dict) -> bool: print("Uploading assets: {}".format(pathnames)) asset_url = github_json['upload_url'].replace('{?name,label}', '') - paths = [_path('{}.asc'.format(p)) for p in pathnames] + paths = [release_info.path('{}.asc'.format(p)) for p in pathnames] headers = { 'Accept': 'application/vnd.github.v3+json', @@ -291,10 +351,10 @@ def upload_assets(pathnames: List[str], github_json: str) -> bool: for path in paths: to_upload = path.read_bytes() - resp = _requests.post( + resp = release_info.requests_module.post( "{}?name={}".format(asset_url, path.name), headers=headers, - auth=auth, + auth=release_info.auth(), data=to_upload, ) @@ -311,59 +371,48 @@ def upload_assets(pathnames: List[str], github_json: str) -> bool: return True - print("in release") - def download(url, filename): - file_path = _path("/tmp/{}".format(filename)) + file_path = release_info.path("/tmp/{}".format(filename)) - with _requests.get(url, stream=True) as resp, file_path.open( - mode='wb' - ) as download_file: + with release_info.requests_module.get( + url, stream=True + ) as resp, file_path.open(mode='wb') as download_file: shutil.copyfileobj(resp.raw, download_file) return file_path - git_version = "{}{}".format(git_tag_prefix, release_version) - changelog_text = _path(".release.txt.md").read_text() - - print("Pushing changes") - - if git_remote_name: - shell_cmd_runner("git push --follow-tags {}".format(git_remote_name)) - else: - shell_cmd_runner("git push --follow-tags") - - print("Creating release") - headers = {'Accept': 'application/vnd.github.v3+json'} - release_info = build_release_dict( - git_version, - changelog_text, - name="{} {}".format(project, release_version), - ) - base_url = "https://api.github.com/repos/{}/{}/releases".format( - space, project + base_url = "https://api.github.com/repos/{}/{}/releases/tags/{}".format( + release_info.space, release_info.project, release_info.git_version() ) - response = _requests.post( - base_url, headers=headers, auth=auth, json=release_info + + response = release_info.requests_module.get( + base_url, + headers=headers, ) - if response.status_code != 201: + if response.status_code != 200: print("Wrong response status code: {}".format(response.status_code)) print(json.dumps(response.text, indent=4, sort_keys=True)) return False - _path(".release.txt.md").unlink() - github_json = json.loads(response.text) - zip_path = download(github_json['zipball_url'], git_version + ".zip") - tar_path = download(github_json['tarball_url'], git_version + ".tar.gz") + zip_path = download( + github_json['zipball_url'], release_info.git_version() + ".zip" + ) + tar_path = download( + github_json['tarball_url'], release_info.git_version() + ".tar.gz" + ) print("Signing {}".format([zip_path, tar_path])) gpg_cmd = "gpg --default-key {} --detach-sign --armor {}" - shell_cmd_runner(gpg_cmd.format(signing_key, zip_path)) - shell_cmd_runner(gpg_cmd.format(signing_key, tar_path)) + release_info.shell_cmd_runner( + gpg_cmd.format(release_info.signing_key, zip_path) + ) + release_info.shell_cmd_runner( + gpg_cmd.format(release_info.signing_key, tar_path) + ) return upload_assets([zip_path, tar_path], github_json) @@ -397,38 +446,33 @@ def execute( user: str, token: str, ): + info = ReleaseInformation( + release_version=release_version, + nextversion=next_release_version, + project=project, + space=space, + git_signing_key=git_signing_key, + git_tag_prefix=git_tag_prefix, + git_remote_name=git_remote_name, + username=user, + token=token, + signing_key=signing_key, + path=_path, + requests_module=_requests, + version_module=_version, + changelog_module=_changelog, + shell_cmd_runner=shell_cmd_runner, + ) + if command == 'prepare': - return prepare( - release_version, - next_release_version, - project, - space, - signing_key, - git_signing_key, - git_tag_prefix, - shell_cmd_runner, - _path, - _requests, - _version, - _changelog, - ) + return prepare(info) if command == 'release': print("command release") return release( - release_version, - project, - space, - signing_key, - user, - token, - git_tag_prefix, - git_remote_name, - shell_cmd_runner, - _path, - _requests, - _version, - _changelog, + info, ) + if command == 'sign': + return sign(info) raise ValueError("Unknown command: {}".format(command)) diff --git a/tests/release/test_release.py b/tests/release/test_release.py index e47c7a79e..4a3f75e38 100644 --- a/tests/release/test_release.py +++ b/tests/release/test_release.py @@ -205,6 +205,125 @@ def test_not_release_when_updating_version_fails(self): ) self.assertFalse(released) + def test_fail_sign_on_invalid_get_response(self): + fake_path_class = MagicMock(spec=Path) + fake_requests = MagicMock(spec=requests) + fake_get = MagicMock(spec=requests.Response).return_value + fake_get.status_code = 404 + fake_get.text = self.valid_gh_release_response + fake_requests.get.return_value = fake_get + fake_version = MagicMock(spec=version) + fake_version.main.return_value = (True, 'MyProject.conf') + fake_changelog = MagicMock(spec=changelog) + fake_changelog.update.return_value = ('updated', 'changelog') + args = [ + '--release-version', + '0.0.1', + '--next-release-version', + '0.0.2dev', + '--project', + 'testcases', + '--git-remote-name', + 'testremote', + 'sign', + ] + + def runner(_: str): + return StdOutput('') + + released = release.main( + shell_cmd_runner=runner, + _path=fake_path_class, + _requests=fake_requests, + _version=fake_version, + _changelog=fake_changelog, + leave=False, + args=args, + ) + self.assertFalse(released) + + def test_fail_sign_on_upload_fail(self): + fake_path_class = MagicMock(spec=Path) + fake_requests = MagicMock(spec=requests) + fake_get = MagicMock(spec=requests.Response).return_value + fake_get.status_code = 200 + fake_get.text = self.valid_gh_release_response + fake_post = MagicMock(spec=requests.Response).return_value + fake_post.status_code = 500 + fake_post.text = self.valid_gh_release_response + fake_requests.post.return_value = fake_post + fake_requests.get.return_value = fake_get + fake_version = MagicMock(spec=version) + fake_version.main.return_value = (True, 'MyProject.conf') + fake_changelog = MagicMock(spec=changelog) + fake_changelog.update.return_value = ('updated', 'changelog') + args = [ + '--release-version', + '0.0.1', + '--next-release-version', + '0.0.2dev', + '--project', + 'testcases', + '--git-remote-name', + 'testremote', + 'sign', + ] + + def runner(_: str): + return StdOutput('') + + released = release.main( + shell_cmd_runner=runner, + _path=fake_path_class, + _requests=fake_requests, + _version=fake_version, + _changelog=fake_changelog, + leave=False, + args=args, + ) + self.assertFalse(released) + + def test_successfully_sign(self): + fake_path_class = MagicMock(spec=Path) + fake_requests = MagicMock(spec=requests) + fake_get = MagicMock(spec=requests.Response).return_value + fake_get.status_code = 200 + fake_get.text = self.valid_gh_release_response + fake_requests.get.return_value = fake_get + fake_post = MagicMock(spec=requests.Response).return_value + fake_post.status_code = 201 + fake_post.text = self.valid_gh_release_response + fake_requests.post.return_value = fake_post + fake_version = MagicMock(spec=version) + fake_version.main.return_value = (True, 'MyProject.conf') + fake_changelog = MagicMock(spec=changelog) + fake_changelog.update.return_value = ('updated', 'changelog') + args = [ + '--release-version', + '0.0.1', + '--next-release-version', + '0.0.2dev', + '--project', + 'testcases', + '--git-remote-name', + 'testremote', + 'sign', + ] + + def runner(_: str): + return StdOutput('') + + released = release.main( + shell_cmd_runner=runner, + _path=fake_path_class, + _requests=fake_requests, + _version=fake_version, + _changelog=fake_changelog, + leave=False, + args=args, + ) + self.assertTrue(released) + def test_release_to_specific_git_remote(self): fake_path_class = MagicMock(spec=Path) fake_requests = MagicMock(spec=requests) @@ -227,7 +346,12 @@ def test_release_to_specific_git_remote(self): 'testremote', 'release', ] - runner = lambda x: StdOutput('') + + def runner(cmd: str): + if not 'testremote' in cmd: + raise ValueError('unexpected cmd: {}'.format(cmd)) + return StdOutput('') + released = release.main( shell_cmd_runner=runner, _path=fake_path_class, From bb46006b8e07df034d7680047c5a63ba95a41f69 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Fri, 18 Sep 2020 12:58:40 +0200 Subject: [PATCH 2/3] fix incorrect PROJECT_DEV behaviour before this commit in_project_dev was never set to false and therefore the actual project_dev change never occurs when there was a command after set PROJECT_DEV. --- CHANGELOG.md | 3 ++- pontos/release/release.py | 12 +++++++----- pontos/version/cmake_version.py | 5 ++++- tests/version/test_cmake_version.py | 22 ++++++++++++++++++++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b8ec85b5..967cbe540 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - add handling of PROJECT_DEV_VERSION in CMakeLists.txt if set [#32](https://github.com/greenbone/pontos/pull/32) ### Changed - set releasename to projectname version [#25](https://github.com/greenbone/pontos/pull/25) -- separate signing tar and zipballs from release into a own command `sign` [#](https://github.com/greenbone/pontos/pull/xx) +- separate signing tar and zipballs from release into a own command `sign` [#33](https://github.com/greenbone/pontos/pull/33) ### Deprecated ### Removed ### Fixed +- project_dev handling was not working when there was a command after the set[#33](https://github.com/greenbone/pontos/pull/33) [Unreleased]: https://github.com/greenbone/pontos/compare/v0.3.0...HEAD diff --git a/pontos/release/release.py b/pontos/release/release.py index 5a01131da..948febabc 100644 --- a/pontos/release/release.py +++ b/pontos/release/release.py @@ -169,16 +169,18 @@ def parse(args=None): def update_version( to: str, _version: version, *, develop: bool ) -> Tuple[bool, str]: - develop_arg = '--develop' if develop else '' - executed, filename = _version.main( - False, args=["--quiet", develop_arg, "update", to] - ) + args = ['--quiet'] + args.append('update') + args.append(to) + if develop: + args.append('--develop') + executed, filename = _version.main(False, args=args) if not executed: if filename == "": print("No project definition found.") else: - print("Unable to update version {} in {}", to, filename) + print("Unable to update version {} in {}".format(to, filename)) return executed, filename diff --git a/pontos/version/cmake_version.py b/pontos/version/cmake_version.py index e3d1e7ced..6f5311dd5 100644 --- a/pontos/version/cmake_version.py +++ b/pontos/version/cmake_version.py @@ -17,6 +17,7 @@ import re +import traceback from typing import Tuple, Generator, Union from pathlib import Path @@ -63,6 +64,7 @@ def run(self, args=None) -> Union[int, str]: elif commandline_arguments.command == 'verify': self.verify_version(commandline_arguments.version) except VersionError as e: + traceback.print_exc() return str(e) return 0 @@ -72,6 +74,7 @@ def __print(self, *args): print(*args) def update_version(self, version: str, *, develop: bool = False): + self.__print("in update: {}, {}".format(version, develop)) content = self.__cmake_filepath.read_text() cmvp = CMakeVersionParser(content) previous_version = cmvp.get_current_version() @@ -131,7 +134,6 @@ def update_version(self, new_version: str, *, develop: bool = False) -> str: to_update = self._cmake_content_lines[self._version_line_number] updated = to_update.replace(self._current_version, new_version) - self._cmake_content_lines[self._version_line_number] = updated self._current_version = new_version if self._project_dev_version_line_number: @@ -184,6 +186,7 @@ def _find_version_in_cmake(self, content: str) -> Tuple[int, str]: ): project_dev_version_line_no = lineno project_dev_version = value + in_project_dev_version = False elif in_project and token_type == 'close_bracket': raise ValueError('unable to find cmake version in project.') diff --git a/tests/version/test_cmake_version.py b/tests/version/test_cmake_version.py index d3f0d1231..56384227e 100644 --- a/tests/version/test_cmake_version.py +++ b/tests/version/test_cmake_version.py @@ -140,6 +140,28 @@ def test_update_project_dev_version(self): ), ) + def test_update_project_dev_version_when_succeeded_by_another_set(self): + test_cmake_lists = """ + cmake_minimum_required(VERSION 3.1) + + project(hello_world VERSION 41.41.41) + set(PROJECT_DEV_VERSION 1) + + add_executable(app main.c) + """ + under_test = CMakeVersionParser(test_cmake_lists) + + self.assertEqual(under_test._project_dev_version_line_number, 4) + self.assertEqual(under_test._project_dev_version, '1') + result = under_test.update_version('41.41.41', develop=False) + self.assertEqual(under_test._project_dev_version, '0') + self.assertEqual( + result, + test_cmake_lists.replace( + 'PROJECT_DEV_VERSION 1', 'PROJECT_DEV_VERSION 0' + ), + ) + def test_get_current_version_multiline_project_combined_token(self): under_test = CMakeVersionParser( "project\n(\nDESCRIPTION something VERSION 2.3.4 LANGUAGES c\n)" From 640d40193e4737fbc1080af7e3609e5f08e0c0a6 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Fri, 18 Sep 2020 14:45:10 +0200 Subject: [PATCH 3/3] change RELEASE.md --- RELEASE.md | 131 +++++++++++++++++++++++------------------------------ 1 file changed, 57 insertions(+), 74 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index ba455e408..9286d202f 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -42,14 +42,32 @@ first time. [testpypi] repository = https://test.pypi.org/legacy/ username = + ``` + +## Create a GitHub Token for uploading the release files -## Prepare testing the Release +This step is only necessary if the token has to be created for the first time or +if it has been lost. -* Fetch upstream changes and create release branch +* Open Github Settings at https://github.com/settings/tokens +* Create a new token +* Copy token and store it carefully +* Export token and GitHub user name in your current shell ```sh + export GITHUB_TOKEN= + export GITHUB_NAME= + ``` + + +## Prepare testing the to be released version + +* Fetch upstream changes + + ```sh + git remote add upstream git@github.com:greenbone/pontos.git git fetch upstream - git checkout -b create-new-release upstream/master + git rebase update/master ``` * Get the current version number @@ -58,10 +76,10 @@ first time. poetry run python -m pontos.version show ``` -* Update the version number to some alpha version e.g. +* Update the version number to some dev version e.g. ```sh - poetry run python -m pontos.version update 2.2.3a1 + poetry run python -m pontos.version update 20.8.2dev1 ``` ## Uploading to the PyPI Test Instance @@ -79,22 +97,28 @@ first time. twine upload -r testpypi dist/* ``` -* Check if the package is available at +* Check if the package is available at . + +## Testing the Uploaded Package -* Create a test directory +* Create a test directory: ```sh mkdir pontos-install-test cd pontos-install-test python3 -m venv test-env - source ./test-env/bin/activate + source test-env/bin/activate pip install -U pip # ensure the environment uses a recent version of pip pip install --pre -I --extra-index-url https://test.pypi.org/simple/ pontos - python -c "from pontos.version import __version__; print(__version__)" - python -m pontos.version show ``` -* Remove test environment +* Check install version with a Python script: + + ```sh + python3 -c "from gvm import __version__; print(__version__)" + ``` + +* Remove test environment: ```sh deactivate @@ -104,97 +128,56 @@ first time. ## Prepare the Release -* Determine new release version number - - If the output is something like `2.2.3.dev1` or `2.2.3a1`, the new version - should be `2.2.3`. - -* Update to new version number (`` must be replaced by the version - from the last step) +* Run pontos-release prepare ```sh - cd path/to/git/clone/of/pontos - poetry run python -m pontos.version update + poetry run pontos-release --release-version --next-release-version --project pontos --space greenbone --git-signing-key --git-remote-name upstream prepare ``` -* Update the `CHANGELOG.md` file: - * Change `[unreleased]` to new release version. - * Add a release date. - * Update reference to Github diff. - * Remove empty sub sections like *Deprecated*. - -* Create a git commit: +* Check git log and tag - ```sh - git add . - git commit -m "Prepare release " ``` + git log -p -## Performing the Release on GitHub - -* Create a pull request (PR) for the earlier commit: - - ```sh - git push origin + # is the changelog correct? + # does the version look right? + # does the tag point to the correct commit? ``` - Open GitHub and create a PR against -* Update after PR is merged +* If something did go wrong delete the tag, revert the commits and remove the + temporary file for the release changelog - ```sh - git fetch upstream - git rebase upstream/master master ``` - -* Create a git tag - - ```sh - git tag v + git tag -d v + git reset --hard + rm .release.txt.md ``` - or even signed with your gpg key - - ```sh - git tag -s v - ``` +## Create the Release -* Push tag to GitHub +* Run pontos-release release ```sh - git push --tags upstream + poetry run pontos-release --release-version --next-release-version --project pontos --space greenbone --git-signing-key --git-remote-name upstream release ``` ## Uploading to the 'real' PyPI * Uploading to PyPI is done automatically by pushing a git tag via CircleCI -* Check if new version is available at +* Check if new version is available at . -## Bumping `master` Branch to the Next Version +## Check the Release -* Update to a Development Version +* Check the Github release: - The next version should contain an incremented minor version and a dev suffix - e.g. 2.3.0.dev1 + See https://github.com/greenbone/pontos/releases - ```sh - poetry run python -m pontos.version update - ``` +## Sign tar and zipball -* Create a commit +* May run pontos-release sign - ```sh - git commit -m "Update version after release" - ``` - -* Push changes to GitHub ```sh - git push upstream + poetry run pontos-release --release-version --next-release-version --project pontos --space greenbone sign ``` - -## Announcing the Release - -* Create a Github release: - - See https://help.github.com/articles/creating-releases/