diff --git a/CHANGELOG.md b/CHANGELOG.md index 27cfbdf..9231b1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v0.18.0 (2023-12-12) + +- Adds a `version` parameter which can override the automatically detected version of a formula with an explicit value + ## v0.17.0 (2023-10-26) - Upgrades from Python 3.11 to Python 3.12 diff --git a/README.md b/README.md index 0cb4692..9b4250f 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,13 @@ jobs: # Optional - string custom_require: custom_download_strategy + # Override the automatically detected version of a formula with an explicit value. + # This option should only be used if Homebrew cannot automatically detect the version when generating + # the Homebrew formula. Including this when not necessary could lead to uninstallable formula that may + # not pass `brew audit` due to mismatched or redundant version strings + # Optional - string + version: '1.2.0' + # Adds URL and checksum targets for different OS and architecture pairs. Using this option assumes # a tar archive exists on your GitHub repo with the following URL pattern (this cannot be customized): # https://github.com/{GITHUB_OWNER}/{REPO_NAME}/releases/download/{TAG}/{REPO_NAME}-{VERSION}-{OPERATING_SYSTEM}-{ARCHITECTURE}.tar.gz' diff --git a/action.yml b/action.yml index 429ff45..ca4ada4 100644 --- a/action.yml +++ b/action.yml @@ -40,6 +40,9 @@ inputs: custom_require: description: "Allows you to add a custom require_relative at the top of the formula template." required: false + version: + description: "Override the automatically detected version of a formula with an explicit value." + required: false target_darwin_amd64: description: "Add a custom URL/checksum target for AMD64 Darwin builds." required: false diff --git a/homebrew_releaser/_version.py b/homebrew_releaser/_version.py index 435d64b..5ec52a9 100644 --- a/homebrew_releaser/_version.py +++ b/homebrew_releaser/_version.py @@ -1 +1 @@ -__version__ = '0.17.0' +__version__ = '0.18.0' diff --git a/homebrew_releaser/app.py b/homebrew_releaser/app.py index e114eda..21220aa 100644 --- a/homebrew_releaser/app.py +++ b/homebrew_releaser/app.py @@ -19,6 +19,7 @@ TARGET_DARWIN_ARM64, TARGET_LINUX_AMD64, TARGET_LINUX_ARM64, + VERSION, ) from homebrew_releaser.formula import Formula from homebrew_releaser.git import Git @@ -73,7 +74,7 @@ def run_github_action(): url=f'https://api.github.com/repos/{GITHUB_OWNER}/{GITHUB_REPO}/releases/latest' ).json() assets = latest_release['assets'] - version = latest_release['tag_name'] + version = VERSION or latest_release['tag_name'] version_no_v = version.lstrip('v') logger.info(f'Latest release ({version}) successfully identified!') @@ -144,6 +145,7 @@ def run_github_action(): TEST, DOWNLOAD_STRATEGY, CUSTOM_REQUIRE, + version_no_v if VERSION else None, ) Utils.write_file(os.path.join(HOMEBREW_TAP, FORMULA_FOLDER, f'{repository["name"]}.rb'), template, 'w') diff --git a/homebrew_releaser/constants.py b/homebrew_releaser/constants.py index f428d0e..3cec292 100644 --- a/homebrew_releaser/constants.py +++ b/homebrew_releaser/constants.py @@ -10,6 +10,7 @@ ) # Must check for string `false` since GitHub Actions passes the bool as a string DOWNLOAD_STRATEGY = os.getenv('INPUT_DOWNLOAD_STRATEGY') CUSTOM_REQUIRE = os.getenv('INPUT_CUSTOM_REQUIRE') +VERSION = os.getenv('INPUT_VERSION') # App Constants LOGGER_NAME = 'homebrew-releaser' diff --git a/homebrew_releaser/formula.py b/homebrew_releaser/formula.py index 6dbffe9..d84f964 100644 --- a/homebrew_releaser/formula.py +++ b/homebrew_releaser/formula.py @@ -31,6 +31,7 @@ def generate_formula_data( test: Optional[str] = None, download_strategy: Optional[str] = None, custom_require: Optional[str] = None, + version: Optional[str] = None, ) -> str: """Generates the formula data for Homebrew. @@ -130,6 +131,9 @@ class {{class_name}} < Formula homepage "https://github.com/{{owner}}/{{repo_name}}" url "{{tar_url}}"{{# download_strategy}}, using: {{download_strategy}}{{/ download_strategy}} sha256 "{{autogenerated_tar_checksum}}" + {{# version}} + version "{{version}}" + {{/ version}} {{# license_type}} license "{{license_type}}" {{/ license_type}} @@ -208,6 +212,7 @@ def install 'test_instructions': test.strip() if test else None, 'download_strategy': download_strategy, 'custom_require': custom_require, + 'version': version, 'darwin_amd64_url': darwin_amd64_url, 'darwin_amd64_checksum': darwin_amd64_checksum, 'darwin_arm64_url': darwin_arm64_url, diff --git a/homebrew_releaser/readme_updater.py b/homebrew_releaser/readme_updater.py index 26925e7..aa2b767 100644 --- a/homebrew_releaser/readme_updater.py +++ b/homebrew_releaser/readme_updater.py @@ -93,13 +93,11 @@ def generate_table(formulas: List) -> str: rows = [] for formula in formulas: - rows.append( - [ - f'[{formula["name"]}]({formula.get("homepage")})', - formula.get('desc'), - f'`brew install {formula["name"]}`', - ] - ) + rows.append([ + f'[{formula["name"]}]({formula.get("homepage")})', + formula.get('desc'), + f'`brew install {formula["name"]}`', + ]) table = pretty_tables.create( headers=headers, @@ -181,7 +179,7 @@ def replace_table_contents(file_content: _io.TextIOWrapper, old_table: str, new_ @staticmethod def does_readme_exist(homebrew_tap: str) -> Optional[str]: - """Determines the README file to open. The README file must either be: + """Determines the README file to open. The README file must either: 1. Have the file extension of `.md` 2. Reside in the root of a project diff --git a/justfile b/justfile index b0171a7..b2f4bf5 100644 --- a/justfile +++ b/justfile @@ -21,10 +21,6 @@ audit: bandit: {{VIRTUAL_BIN}}/bandit -r {{PROJECT_NAME}}/ -# Builds the project in preparation for release -build: - {{VIRTUAL_BIN}}/python -m build - # Runs the Black Python formatter against the project black: {{VIRTUAL_BIN}}/black {{PROJECT_NAME}}/ {{TEST_DIR}}/ diff --git a/setup.py b/setup.py index 41a7e42..1526b03 100644 --- a/setup.py +++ b/setup.py @@ -24,13 +24,11 @@ DEV_REQUIREMENTS = [ 'bandit == 1.7.*', 'black == 23.*', - 'build == 0.10.*', 'flake8 == 6.*', 'isort == 5.*', - 'mypy == 1.5.*', + 'mypy == 1.7.*', 'pytest == 7.*', 'pytest-cov == 4.*', - 'twine == 4.*', 'types-requests', ] diff --git a/test/formulas/test_generate_formula_override_version.rb b/test/formulas/test_generate_formula_override_version.rb new file mode 100644 index 0000000..44c9b58 --- /dev/null +++ b/test/formulas/test_generate_formula_override_version.rb @@ -0,0 +1,16 @@ +# typed: true +# frozen_string_literal: true + +# This file was generated by Homebrew Releaser. DO NOT EDIT. +class TestGenerateFormulaOverrideVersion < Formula + desc "Release scripts, binaries, and executables to github" + homepage "https://github.com/Justintime50/test-generate-formula-override-version" + url "https://github.com/Justintime50/test-generate-formula-override-version/archive/refs/tags/v0.1.0.tar.gz" + sha256 "0000000000000000000000000000000000000000000000000000000000000000" + version "9.8.7" + license "MIT" + + def install + bin.install "src/secure-browser-kiosk.sh" => "secure-browser-kiosk" + end +end diff --git a/test/unit/test_formula.py b/test/unit/test_formula.py index 9b93150..1cf1703 100644 --- a/test/unit/test_formula.py +++ b/test/unit/test_formula.py @@ -67,14 +67,12 @@ def test_generate_formula(): owner=USERNAME, repo_name=mock_repo_name, repository=repository, - checksums=[ - { - f'{mock_repo_name}.tar.gz': { - 'checksum': CHECKSUM, - 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa - }, - } - ], + checksums=[{ + f'{mock_repo_name}.tar.gz': { + 'checksum': CHECKSUM, + 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa + }, + }], install=INSTALL, tar_url=mock_tar_url, depends_on=DEPENDS_ON, @@ -123,14 +121,12 @@ def test_generate_formula_no_article_description(): owner=USERNAME, repo_name=mock_repo_name, repository=repository, - checksums=[ - { - f'{mock_repo_name}.tar.gz': { - 'checksum': CHECKSUM, - 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa - }, - } - ], + checksums=[{ + f'{mock_repo_name}.tar.gz': { + 'checksum': CHECKSUM, + 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa + }, + }], install=INSTALL, tar_url=mock_tar_url, depends_on=None, @@ -162,14 +158,12 @@ def test_generate_formula_formula_name_starts_description(): owner=USERNAME, repo_name=mock_repo_name, repository=repository, - checksums=[ - { - f'{mock_repo_name}.tar.gz': { - 'checksum': CHECKSUM, - 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa - }, - } - ], + checksums=[{ + f'{mock_repo_name}.tar.gz': { + 'checksum': CHECKSUM, + 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa + }, + }], install=INSTALL, tar_url=mock_tar_url, depends_on=None, @@ -199,14 +193,12 @@ def test_generate_formula_no_depends_on(): owner=USERNAME, repo_name=mock_repo_name, repository=repository, - checksums=[ - { - f'{mock_repo_name}.tar.gz': { - 'checksum': CHECKSUM, - 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa - }, - } - ], + checksums=[{ + f'{mock_repo_name}.tar.gz': { + 'checksum': CHECKSUM, + 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa + }, + }], install=INSTALL, tar_url=mock_tar_url, depends_on=None, @@ -236,14 +228,12 @@ def test_generate_formula_no_test(): owner=USERNAME, repo_name=mock_repo_name, repository=repository, - checksums=[ - { - f'{mock_repo_name}.tar.gz': { - 'checksum': CHECKSUM, - 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa - }, - } - ], + checksums=[{ + f'{mock_repo_name}.tar.gz': { + 'checksum': CHECKSUM, + 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa + }, + }], install=INSTALL, tar_url=mock_tar_url, depends_on=DEPENDS_ON, @@ -511,14 +501,12 @@ def test_generate_formula_string_false_configs(): owner=USERNAME, repo_name=mock_repo_name, repository=repository, - checksums=[ - { - f'{mock_repo_name}.tar.gz': { - 'checksum': CHECKSUM, - 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa - }, - } - ], + checksums=[{ + f'{mock_repo_name}.tar.gz': { + 'checksum': CHECKSUM, + 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa + }, + }], install=INSTALL, tar_url=mock_tar_url, depends_on=None, @@ -550,14 +538,12 @@ def test_generate_formula_empty_fields(): owner=USERNAME, repo_name=mock_repo_name, repository=repository, - checksums=[ - { - f'{mock_repo_name}.tar.gz': { - 'checksum': CHECKSUM, - 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa - }, - } - ], + checksums=[{ + f'{mock_repo_name}.tar.gz': { + 'checksum': CHECKSUM, + 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa + }, + }], install=INSTALL, tar_url=mock_tar_url, depends_on=None, @@ -636,3 +622,37 @@ def test_generate_formula_download_strategy(): assert formula.count(', using: CustomDownloadStrategy') == 5 assert 'require_relative "../formula_imports/mock_download_strategy"' in formula + + +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. + """ + formula_filename = f'{inspect.stack()[0][3]}.rb' + mock_repo_name = formula_filename.replace('_', '-').replace('.rb', '') + mock_tar_url = f'https://github.com/{USERNAME}/{mock_repo_name}/archive/refs/tags/v0.1.0.tar.gz' + + repository = { + 'description': DESCRIPTION, + 'license': LICENSE, + } + + formula = Formula.generate_formula_data( + owner=USERNAME, + repo_name=mock_repo_name, + repository=repository, + checksums=[{ + f'{mock_repo_name}.tar.gz': { + 'checksum': CHECKSUM, + 'url': f'https://github.com/justintime50/{mock_repo_name}/releases/download/{VERSION}/{mock_repo_name}-{VERSION}.tar.gz', # noqa + }, + }], + install=INSTALL, + tar_url=mock_tar_url, + version='9.8.7', + ) + + record_formula(formula_path, formula_filename, formula) + + assert '9.8.7' in formula diff --git a/test/unit/test_git.py b/test/unit/test_git.py index a6849cb..dd2bfd1 100644 --- a/test/unit/test_git.py +++ b/test/unit/test_git.py @@ -19,33 +19,31 @@ def test_setup(mock_subprocess): commit_email = 'user@example.com' Git.setup(homebrew_owner, commit_email, homebrew_owner, homebrew_tap) - mock_subprocess.assert_has_calls( - [ - call( - [ - 'git', - 'clone', - '--depth=1', - 'https://x-access-token:123@github.com/Justintime50/homebrew-formulas.git', - ], - stderr=-2, - text=True, - timeout=30, - ), - call( - ['git', '-C', 'homebrew-formulas', 'config', 'user.name', '"Justintime50"'], - stderr=-2, - text=True, - timeout=30, - ), - call( - ['git', '-C', 'homebrew-formulas', 'config', 'user.email', 'user@example.com'], - stderr=-2, - text=True, - timeout=30, - ), - ] - ) + mock_subprocess.assert_has_calls([ + call( + [ + 'git', + 'clone', + '--depth=1', + 'https://x-access-token:123@github.com/Justintime50/homebrew-formulas.git', + ], + stderr=-2, + text=True, + timeout=30, + ), + call( + ['git', '-C', 'homebrew-formulas', 'config', 'user.name', '"Justintime50"'], + stderr=-2, + text=True, + timeout=30, + ), + call( + ['git', '-C', 'homebrew-formulas', 'config', 'user.email', 'user@example.com'], + stderr=-2, + text=True, + timeout=30, + ), + ]) @patch('subprocess.check_output') diff --git a/test/unit/test_readme_updater.py b/test/unit/test_readme_updater.py index 4232428..9c72494 100644 --- a/test/unit/test_readme_updater.py +++ b/test/unit/test_readme_updater.py @@ -75,7 +75,7 @@ def test_format_formula_data(): """ formulas = ReadmeUpdater.format_formula_data('./test') - assert len(formulas) == 12 + assert len(formulas) == 13 assert formulas[0] == { 'name': 'test-generate-formula', 'desc': 'Tool to release scripts, binaries, and executables to github',