Skip to content

Commit

Permalink
Merge pull request #49 from Justintime50/fix_class_name
Browse files Browse the repository at this point in the history
fix: class name generation when digits are present
  • Loading branch information
Justintime50 authored Oct 10, 2024
2 parents de8f639 + 0dd65c5 commit c0eec8b
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Next Release

- Upgrades Python from 3.12 to 3.13
- Fixes a bug that would not generate a proper class name for formula if it contained a digit (closes #47)

## v0.19.0 (2024-07-11)

Expand Down
21 changes: 19 additions & 2 deletions homebrew_releaser/formula.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def generate_formula_data(
"""Generates the formula data for Homebrew.
We attempt to ensure generated formula will pass `brew audit --strict --online` if given correct inputs:
- Proper class name
- Proper Homebrew/Ruby class name
- 80 characters or less desc field (alphanumeric characters and does not start with
an article or the name of the formula)
- Homepage
Expand All @@ -53,7 +53,7 @@ def generate_formula_data(

max_desc_field_length = 80 # `brew audit` wants no more than 80 characters in the desc field

class_name = re.sub(r'[-_. ]+', '', repo_name.title())
class_name = Formula._generate_class_name(repo_name)
license_type = repository['license'].get('spdx_id', '') if repository.get('license') else ''
description = (
re.sub(r'[.!]+', '', repository.get('description', '')[:max_desc_field_length]).strip().capitalize()
Expand Down Expand Up @@ -244,3 +244,20 @@ def install
logger.debug(rendered_template)

return rendered_template

@staticmethod
def _generate_class_name(repo_name: str) -> str:
"""Generates a Homebrew compatible formula class name.
1. Converts a repo name to a title-cased class name for Ruby.
2. Ensures that digits in a name do not title case the next word which is not compatible with Homebrew.
"""
return re.sub(
r'[-_. ]+',
'',
re.sub(
r"\d([A-Z])",
lambda x: x.group(0).lower(),
repo_name.title(),
),
)
78 changes: 49 additions & 29 deletions test/unit/test_formula.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import os
from unittest.mock import patch

import pytest

from homebrew_releaser.formula import Formula


Expand All @@ -21,7 +23,7 @@
LICENSE = {'spdx_id': 'MIT'}


def record_formula(formula_path: str, formula_name: str, formula_data: str):
def _record_formula(formula_path: str, formula_name: str, formula_data: str):
"""Read from an existing formula file or create a new formula file if it's not present.
Tests using this function will generate a formula into a file (similar to how
Expand All @@ -47,7 +49,7 @@ def test_generate_formula():
"""Tests that we generate the formula content correctly when all parameters are passed
(except a matrix so that we can test the auto-generate URL/checksum from GitHub).
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -81,7 +83,7 @@ def test_generate_formula():
test=TEST,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

# The following assertions are explicitly listed as the "gold standard" for generic formula generation
assert (
Expand Down Expand Up @@ -119,7 +121,7 @@ def test_generate_formula_no_article_description():
"""Tests that we generate the formula content correctly (when there is no article
that starts the description field).
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -149,7 +151,7 @@ def test_generate_formula_no_article_description():
test=None,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'desc "Release scripts, binaries, and executables to github"' in formula

Expand All @@ -158,7 +160,7 @@ def test_generate_formula_formula_name_starts_description():
"""Tests that we generate the formula content correctly (when the name of the formula
starts the description field) - it should get stripped out per `brew audit`.
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -188,15 +190,15 @@ def test_generate_formula_formula_name_starts_description():
test=None,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'desc "Is a tool"' in formula


def test_generate_formula_no_depends_on():
"""Tests that we generate the formula content correctly (when no depends_on given).
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -225,15 +227,15 @@ def test_generate_formula_no_depends_on():
test=TEST,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'depends_on' not in formula


def test_generate_formula_no_test():
"""Tests that we generate the formula content correctly (when there is no test).
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -262,7 +264,7 @@ def test_generate_formula_no_test():
test=None,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'test do' not in formula

Expand All @@ -274,7 +276,7 @@ def test_generate_formula_no_test():
def test_generate_formula_complete_matrix():
"""Tests that we generate the formula content correctly when we provide a complete OS matrix.
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -327,7 +329,7 @@ def test_generate_formula_complete_matrix():
test=TEST,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert formula.count('url') == 5
assert formula.count('sha256') == 5
Expand All @@ -342,7 +344,7 @@ def test_generate_formula_complete_matrix():
def test_generate_formula_darwin_matrix():
"""Tests that we generate the formula content correctly when we provide a Darwin matrix.
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -383,7 +385,7 @@ def test_generate_formula_darwin_matrix():
test=None,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'on_macos' in formula
assert 'on_intel' in formula
Expand All @@ -396,7 +398,7 @@ def test_generate_formula_darwin_matrix():
def test_generate_formula_linux_matrix():
"""Tests that we generate the formula content correctly when we provide a Linux matrix.
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -437,7 +439,7 @@ def test_generate_formula_linux_matrix():
test=None,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'on_macos' not in formula
assert 'on_intel' in formula
Expand All @@ -451,7 +453,7 @@ def test_one_of_each_matrix():
"""Tests that we generate the formula content correctly when we specify only one arch from each OS.
This test helps ensure that we properly spaces the `on_` functions correctly when only one is present.
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -492,7 +494,7 @@ def test_one_of_each_matrix():
test=None,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'on_macos' in formula
assert 'on_intel' in formula
Expand All @@ -508,7 +510,7 @@ def test_generate_formula_string_false_configs():
"""Tests that we generate the formula content correctly when the user specifies `false` on
boolean flags because GitHub Actions passes them in as strings...
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -537,7 +539,7 @@ def test_generate_formula_string_false_configs():
test=None,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'on_macos' not in formula
assert 'on_intel' not in formula
Expand All @@ -550,7 +552,7 @@ def test_generate_formula_empty_fields():
such as the `license` or the `description` - license should not be included, desc should
be a placeholder.
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand All @@ -576,7 +578,7 @@ def test_generate_formula_empty_fields():
test=None,
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'desc "NA"' in formula
assert 'license' not in formula
Expand All @@ -589,7 +591,7 @@ def test_generate_formula_empty_fields():
def test_generate_formula_download_strategy():
"""Tests that we generate the formula content correctly when there a custom download strategy specified.
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -644,7 +646,7 @@ def test_generate_formula_download_strategy():
custom_require='../formula_imports/mock_download_strategy',
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert formula.count(', using: CustomDownloadStrategy') == 5
assert 'require_relative "../formula_imports/mock_download_strategy"' in formula
Expand All @@ -653,7 +655,7 @@ def test_generate_formula_download_strategy():
def test_generate_formula_override_version():
"""Tests that we generate the formula content correctly (when the version is overridden).
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -681,15 +683,15 @@ def test_generate_formula_override_version():
version='9.8.7',
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert '9.8.7' in formula


def test_generate_formula_formula_includes():
"""Tests that we generate the formula content correctly when using the formula_includes param.
NOTE: See docstring in `record_formula` for more details on how recording formulas works.
NOTE: See docstring in `_record_formula` for more details on how recording formulas works.
"""
formula_filename = f'{inspect.stack()[0][3]}.rb'
mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '')
Expand Down Expand Up @@ -717,6 +719,24 @@ def test_generate_formula_formula_includes():
formula_includes='include Language::Python::Virtualenv',
)

record_formula(formula_path, formula_filename, formula)
_record_formula(formula_path, formula_filename, formula)

assert 'include Language::Python::Virtualenv' in formula


@pytest.mark.parametrize(
'repo_name,expected_class_name',
[
('foobarbaz', 'Foobarbaz'),
('foo2bar', 'Foo2bar'), # Homebrew doesn't want "bar" capitalized after a digit
('foo-bar-baz', 'FooBarBaz'),
('foo_bar_baz', 'FooBarBaz'),
('foobar2', 'Foobar2'),
('2foobar', '2foobar'),
],
)
def test_generate_class_name(repo_name, expected_class_name):
"""Tests that we generate a proper Homebrew/Ruby class name for formula under various situations."""
class_name = Formula._generate_class_name(repo_name)

assert class_name == expected_class_name

0 comments on commit c0eec8b

Please sign in to comment.