Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add options for non-Python projects #65

Merged
merged 2 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions release_tools/publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@
help="Do not remove changelog entries from the repository.")
@click.option('--remote-branch', 'remote_branch', default="master",
help="Remote branch to push. Default 'master'.")
def publish(version, author, remote, only_push, no_cleanup, remote_branch):
@click.option('--add-all', is_flag=True,
sduenas marked this conversation as resolved.
Show resolved Hide resolved
help="Add all changed files to the release commit.")
def publish(version, author, remote, only_push, no_cleanup, remote_branch, add_all):
"""Publish a new release.

This script will generate a new release in the repository.
Expand All @@ -71,6 +73,10 @@ def publish(version, author, remote, only_push, no_cleanup, remote_branch):
When '--no-cleanup' argument is specified, do not remove changelog
entries.

By default, the release commit will include the version, pyproject,
release notes, news and authors files. To add all changed files to
the release commit use the `--add-all` flag.

VERSION: version of the new release.

AUTHOR: author of the new release (e.g. John Smith <[email protected]>)
Expand All @@ -88,7 +94,7 @@ def publish(version, author, remote, only_push, no_cleanup, remote_branch):
if not only_push:
if not no_cleanup:
remove_unreleased_changelog_entries(project)
add_release_files(project, version)
add_release_files(project, version, add_all)
commit(project, version, author)

if remote:
Expand Down Expand Up @@ -126,28 +132,31 @@ def rollback_add_release_files(project):
pass


def add_release_files(project, version):
def add_release_files(project, version, add_all):
"""Add to the repository all the files needed to publish a release."""

click.echo("Adding files to the release commit...", nl=False)

# Add version file
version_file = project.version_file
if add_all:
project.repo.add_all()
else:
# Add version file
version_file = project.version_file

if not version_file:
rollback_add_release_files(project)
raise click.ClickException("version file not found")
if not version_file:
rollback_add_release_files(project)
raise click.ClickException("version file not found")

project.repo.add(version_file)
project.repo.add(version_file)

# Add pyproject.toml file
pyproject_file = project.pyproject_file
# Add pyproject.toml file
pyproject_file = project.pyproject_file

if not pyproject_file:
rollback_add_release_files(project)
raise click.ClickException("pyproject file not found")
if not pyproject_file:
rollback_add_release_files(project)
raise click.ClickException("pyproject file not found")

project.repo.add(pyproject_file)
project.repo.add(pyproject_file)

# Add release notes file
notes_file = os.path.join(project.releases_path, version + '.md')
Expand Down
4 changes: 4 additions & 0 deletions release_tools/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ def add(self, filename):
cmd = ['git', 'add', filename]
self._exec(cmd, cwd=self.dirpath, env=self.gitenv)

def add_all(self):
cmd = ['git', 'add', '-A']
self._exec(cmd, cwd=self.dirpath, env=self.gitenv)

def rm(self, filename):
cmd = ['git', 'rm', '-f', filename]
self._exec(cmd, cwd=self.dirpath, env=self.gitenv)
Expand Down
27 changes: 20 additions & 7 deletions release_tools/semverup.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@
help="Increase only the defined version.")
@click.option('--pre-release', is_flag=True,
help="Create a new release candidate version.")
def semverup(dry_run, bump_version, pre_release):
@click.option('--current-version',
help="Use the given version instead of the version file.")
def semverup(dry_run, bump_version, pre_release, current_version):
"""Increment version number following semver specification.

This script will bump up the version number of a package in a
Expand All @@ -67,6 +69,10 @@ def semverup(dry_run, bump_version, pre_release):
properly, the script will get the type of every unreleased change
stored under 'releases/unreleased' directory.

If you don't want to use the version number stored in '_version.py',
use '--current-version=<VERSION NUMBER>' with the one you would like
to use.

Additionally, 'pyproject' file will also be updated. Take into
account this file must be tracked by the repository.

Expand Down Expand Up @@ -95,12 +101,17 @@ def semverup(dry_run, bump_version, pre_release):
except RepositoryError as e:
raise click.ClickException(e)

# Get the current version number
version_file = find_version_file(project)
current_version = read_version_number(version_file)

# Get the pyproject file
pyproject_file = find_pyproject_file(project)
if current_version:
try:
current_version = semver.parse_version_info(current_version)
except ValueError:
msg = "version number '{}' is not a valid semver string"
msg = msg.format(current_version)
raise click.ClickException(msg)
else:
# Get the current version number
version_file = find_version_file(project)
current_version = read_version_number(version_file)

# Determine the new version and produce the output
if bump_version:
Expand All @@ -109,6 +120,8 @@ def semverup(dry_run, bump_version, pre_release):
new_version = determine_new_version_number(project, current_version, pre_release)

if not dry_run:
# Get the pyproject file
pyproject_file = find_pyproject_file(project)
write_version_number(version_file, new_version)
write_version_number_pyproject(pyproject_file,
new_version)
Expand Down
11 changes: 11 additions & 0 deletions releases/unreleased/option-to-add-all-files-to-release-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: Option to add all files to release commit
category: added
author: Eva Millán <[email protected]>
issue: null
notes: >
To include all changed files in the release commit,
add the `--add-all` flag to the `publish` command.
This is useful for non-Python projects that may
need to change the version number in files other
than '_version.py'.
10 changes: 10 additions & 0 deletions releases/unreleased/set-current-version-manually.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: Set current version manually in semverup
category: added
author: Eva Millán <[email protected]>
issue: null
notes: >
Users can pass the current project version number to the
`semverup` command using the `--current-version=VERSION_NUMBER`
argument. This is useful for non-Python projects because it avoids
having to look for the version in a `_version.py` file.
34 changes: 34 additions & 0 deletions tests/test_publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,40 @@ def test_publish_different_branch(self, mock_project):
mock_project.return_value.repo.push.assert_any_call('myremote', 'main')
mock_project.return_value.repo.push.assert_any_call('myremote', '0.8.10')

@unittest.mock.patch('release_tools.publish.read_changelog_entries')
@unittest.mock.patch('release_tools.publish.Project')
def test_add_all(self, mock_project, mock_read_changelog):
"""Test if '--add-all' adds all files to the release commit."""
version_number = "0.8.10"

runner = click.testing.CliRunner()

with runner.isolated_filesystem() as fs:
os.path.join(fs, 'package.json')
os.path.join(fs, 'Dockerfile')
notes_file = os.path.join(fs, version_number + '.md')
news_file = os.path.join(fs, 'NEWS')
authors_file = os.path.join(fs, 'AUTHORS')

self.setup_release_notes(fs, notes_file, newsfile=news_file, authorsfile=authors_file)

mock_project.return_value.unreleased_processed_entries_path = fs
mock_project.return_value.releases_path = fs
mock_project.return_value.news_file = news_file
mock_project.return_value.authors_file = authors_file

# Run the command
result = runner.invoke(publish.publish,
[version_number, "John Smith <[email protected]>",
"--add-all"])
self.assertEqual(result.exit_code, 0)

# Check mock calls
mock_project.return_value.repo.add_all.assert_called()
mock_project.return_value.repo.add.assert_any_call(notes_file)
mock_project.return_value.repo.add.assert_any_call(news_file)
mock_project.return_value.repo.add.assert_any_call(authors_file)
sduenas marked this conversation as resolved.
Show resolved Hide resolved


if __name__ == '__main__':
unittest.main()
50 changes: 50 additions & 0 deletions tests/test_semverup.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
MOCK_REPOSIORY_ERROR = (
"Error: mock repository error"
)
INVALID_CURRENT_VERSION = (
r"Error: version number 'invalid' is not a valid semver string"
)


class TestSemVerUp(unittest.TestCase):
Expand Down Expand Up @@ -879,6 +882,53 @@ def test_get_next_version(self):
bump_version=case[1],
do_prerelease=case[2])

@unittest.mock.patch('release_tools.semverup.Project')
def test_current_version_arg(self, mock_project):
"""Check whether it uses the given --current-version instead of version file"""

runner = click.testing.CliRunner(mix_stderr=False)

with runner.isolated_filesystem() as fs:
version_file = os.path.join(fs, '_version.py')
mock_project.return_value.version_file = version_file

project_file = os.path.join(fs, 'pyproject.toml')
mock_project.return_value.pyproject_file = project_file

dirpath = os.path.join(fs, 'releases', 'unreleased')
mock_project.return_value.unreleased_changes_path = dirpath

self.setup_files(version_file, project_file, "0.1.0")
self.setup_unreleased_entries(dirpath)

# Run the script command
result = runner.invoke(semverup.semverup, args=['--current-version=2.0.0', '--dry-run'])
self.assertEqual(result.exit_code, 0)
self.assertEqual(result.stdout, "2.1.0\n")

@unittest.mock.patch('release_tools.semverup.Project')
def test_current_version_invalid_format(self, mock_project):
"""Check whether it fails when --current-version has an invalid format"""

runner = click.testing.CliRunner(mix_stderr=False)

with runner.isolated_filesystem() as fs:
version_file = os.path.join(fs, '_version.py')
mock_project.return_value.version_file = version_file

dirpath = os.path.join(fs, 'releases', 'unreleased')
mock_project.return_value.unreleased_changes_path = dirpath

self.setup_version_file(version_file, "0.1.0")
self.setup_unreleased_entries(dirpath)

# Run the script command
result = runner.invoke(semverup.semverup, args=['--current-version=invalid'])
self.assertEqual(result.exit_code, 1)

lines = result.stderr.split('\n')
self.assertRegex(lines[-2], INVALID_CURRENT_VERSION)


if __name__ == '__main__':
unittest.main()