From a3d55c4d5472d8fe04e9ef8187b25fcaef0b4971 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 16:43:39 -0300 Subject: [PATCH 01/61] feat/intial-v initial code --- README.md | 107 ++++++++++++++++++++++++++++++++++- find_and_replace/__init__.py | 0 find_and_replace/main.py | 48 ++++++++++++++++ images/.DS_Store | Bin 0 -> 6148 bytes images/pypi-package.png | Bin 0 -> 93287 bytes scripts/publish.sh | 20 +++++++ setup.cfg | 16 ++++++ setup.py | 5 ++ 8 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 find_and_replace/__init__.py create mode 100755 find_and_replace/main.py create mode 100644 images/.DS_Store create mode 100644 images/pypi-package.png create mode 100644 scripts/publish.sh create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/README.md b/README.md index ad74a94..754abc3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,105 @@ -# find-and-replace -Python package and pre-commit-hook for finding and replacing string(s) in file(s) +# Find and Replace Template Commit Check + +This Python package provides a pre-commit hook that finds strings in files and replaces them with other strings. + +## Installation + +This is an easy to use package which is already available here https://pypi.org/project/find-and-replace-template-commit-check/: + +![package to use](./images/pypi-package.png "Title") + +You can install the package via pip: + +```bash +pip install find-and-replace-template-commit-check +``` + +## Usage + +To use this package, you need to add it to your pre-commit configuration file (.pre-commit-config.yaml). Here's an example: + +``` +repos: + - repo: https://github.com/andreouellet/pre-commit-find-and-replace-test (To be changed when repo is ready) + rev: test3 + hooks: + - id: find-and-replace + name: find-and-replace + description: Find and replace strings + entry: find-and-replace + language: python + pass_filenames: true + exclude_types: + - binary + args: ["--read-from-file", "true"] + files: README.md + verbose: true + +``` + +Please note you also have a choice of + files: '.*\.md$' +or + files: . + +In this configuration, the find-and-replace hook is set to read search and replacement strings from a file (.project-properties.json by default which should be defined in the root of the project you want to use this package). You can also specify the search and replacement strings directly in the args field (which is not a suggested way). + + +## Run tests + +``` +python3 -m unittest tests/test_main.py + +``` + +## How to run it using installed python package + +``` + pip install find-and-replace-template-commit-check + find-and-replace --config .find-and-replace.json README1.md README2.md +``` + + +## If you need more help with the flags and usage of them + +``` +find-and-replace -h +usage: find-and-replace [-h] [--search SEARCH] [--replacement REPLACEMENT] [--read-from-file READ_FROM_FILE] + [--config REPLACEMENTS_FILE] + [files ...] + +This script performs search and replace operations on one or more files. It supports two modes of operation: Direct Mode and +File Mode. In Direct Mode, you specify the search and replacement strings directly on the command line. In File Mode, the script +reads the search and replacement strings from a JSON file. + +positional arguments: + files Files to perform search and replace + +options: + -h, --help show this help message and exit + --search SEARCH Text to search for + --replacement REPLACEMENT + Text to replace with + --read-from-file READ_FROM_FILE + Read search and replacement strings from file + --config REPLACEMENTS_FILE + Path to the replacements file + +``` + + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## License + +This project is licensed under the terms of the MIT license. + +## Building and Publishing + +To build and publish it to pypi run + +``` +bash scripts/publish.sh +``` diff --git a/find_and_replace/__init__.py b/find_and_replace/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/find_and_replace/main.py b/find_and_replace/main.py new file mode 100755 index 0000000..b06ad99 --- /dev/null +++ b/find_and_replace/main.py @@ -0,0 +1,48 @@ +import unittest +import tempfile +import os +from unittest.mock import patch, mock_open +import json +from main import replace_in_file, main + +class TestReplaceInFile(unittest.TestCase): + def setUp(self): + self.temp_file = tempfile.NamedTemporaryFile(delete=False) + self.temp_file.write(b'Test line 1\nTest line 2\nTest line 3\n') + self.temp_file.close() + + def tearDown(self): + os.unlink(self.temp_file.name) + + def test_replace_in_file(self): + replace_in_file(self.temp_file.name, 'Test', 'Replaced') + with open(self.temp_file.name, 'r') as f: + lines = f.readlines() + self.assertEqual(lines, ['Replaced line 1\n', 'Replaced line 2\n', 'Replaced line 3\n']) + +class TestMain(unittest.TestCase): + @patch('argparse.ArgumentParser.parse_args') + @patch('builtins.open', new_callable=mock_open, read_data='[{"search": "Test", "replacement": "Replaced"}]') + def test_main_with_file_mode(self, mock_file, mock_args): + mock_args.return_value = argparse.Namespace(files=[os.path.join(os.getcwd(), 'test.txt')], find=None, replacement=None, read_from_file=True, config='config.json') + with open('test.txt', 'w') as f: + f.write('Test line 1\nTest line 2\nTest line 3\n') + main() + with open('test.txt', 'r') as f: + lines = f.readlines() + self.assertEqual(lines, ['Replaced line 1\n', 'Replaced line 2\n', 'Replaced line 3\n']) + os.remove('test.txt') + + @patch('argparse.ArgumentParser.parse_args') + def test_main_with_direct_mode(self, mock_args): + mock_args.return_value = argparse.Namespace(files=[os.path.join(os.getcwd(), 'test.txt')], find='Test', replacement='Replaced', read_from_file=False, config=None) + with open('test.txt', 'w') as f: + f.write('Test line 1\nTest line 2\nTest line 3\n') + main() + with open('test.txt', 'r') as f: + lines = f.readlines() + self.assertEqual(lines, ['Replaced line 1\n', 'Replaced line 2\n', 'Replaced line 3\n']) + os.remove('test.txt') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/images/.DS_Store b/images/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..dfb978bceff6a6eac785f6c5231ce7fb2e3acd52 GIT binary patch literal 6148 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 literal 0 HcmV?d00001 diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100644 index 0000000..7797311 --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,20 @@ +#!/bin/bash +rm -r dist +# Check if twine and setuptools are installed +if ! python3 -c "import twine" &> /dev/null; then + echo "twine not found, installing..." + pip install twine +fi + +if ! python3 -c "import setuptools" &> /dev/null; then + echo "setuptools not found, installing..." + pip install setuptools +fi + +# Build the package +python3 setup.py sdist + +# Upload the package to PyPI +echo "Enter your PyPI API token:" +token=$token +twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p $token dist/* diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..0cbc552 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,16 @@ +# setup.cfg +[metadata] +name = find-and-replace-template-commit-check +description = Finds strings in files and replaces them with other strings. +version = 0.8.0 +author = Andre Ouellet +author_email = me@here.com +license = MIT +url = https://example.com + +[options] +packages = find: + +[options.entry_points] +console_scripts = + find-and-replace = find_and_replace.main:main diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ab00c21 --- /dev/null +++ b/setup.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# setup.py +from setuptools import setup + +setup() From 9603a5010058eb483f91159bf09429162e398eb6 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 16:45:18 -0300 Subject: [PATCH 02/61] feat/intial-v initial code --- setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.py b/setup.py index ab00c21..6068493 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# setup.py from setuptools import setup setup() From b529922969a50f0706e637027cf929d862ef987f Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 17:45:45 -0300 Subject: [PATCH 03/61] feat/intial-v initial code --- find_and_replace/main.py | 75 ++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/find_and_replace/main.py b/find_and_replace/main.py index b06ad99..afee58f 100755 --- a/find_and_replace/main.py +++ b/find_and_replace/main.py @@ -1,48 +1,41 @@ -import unittest -import tempfile +# -*- coding: utf-8 -*- import os -from unittest.mock import patch, mock_open +import argparse +import fileinput import json -from main import replace_in_file, main +import sys -class TestReplaceInFile(unittest.TestCase): - def setUp(self): - self.temp_file = tempfile.NamedTemporaryFile(delete=False) - self.temp_file.write(b'Test line 1\nTest line 2\nTest line 3\n') - self.temp_file.close() +def replace_in_file(filename, search, replacement): + with fileinput.FileInput(filename, inplace=True) as file: + for line in file: + print(line.replace(rf"{search}", rf"{replacement}"), end='') - def tearDown(self): - os.unlink(self.temp_file.name) +def main(): + parser = argparse.ArgumentParser(description="This script performs search and replace operations on one or more files. It supports two modes of operation: Direct Mode and File Mode. In Direct Mode, you specify the search and replacement strings directly on the command line. In File Mode, the script reads the search and replacement strings from a JSON file.") + parser.add_argument('files', nargs='*', help='Files to perform search and replace') + parser.add_argument('--find', help='Text to find in files') + parser.add_argument('--replacement', help='Text to replace with in files') + parser.add_argument('--read-from-file', type=bool, default=True, help='Read search and replacement strings from file') + parser.add_argument('--config', default='.find-and-replace.json', help='Path to the config file') + args = parser.parse_args() - def test_replace_in_file(self): - replace_in_file(self.temp_file.name, 'Test', 'Replaced') - with open(self.temp_file.name, 'r') as f: - lines = f.readlines() - self.assertEqual(lines, ['Replaced line 1\n', 'Replaced line 2\n', 'Replaced line 3\n']) + if args.read_from_file: + try: + with open(os.path.join(os.getcwd(), args.config), 'r') as f: + replacements = json.load(f) + except FileNotFoundError: + print(f"Error: {args.config} file not found.") + sys.exit(1) + except json.JSONDecodeError: + print(f"Error: {args.config} is not a valid JSON file.") + sys.exit(1) -class TestMain(unittest.TestCase): - @patch('argparse.ArgumentParser.parse_args') - @patch('builtins.open', new_callable=mock_open, read_data='[{"search": "Test", "replacement": "Replaced"}]') - def test_main_with_file_mode(self, mock_file, mock_args): - mock_args.return_value = argparse.Namespace(files=[os.path.join(os.getcwd(), 'test.txt')], find=None, replacement=None, read_from_file=True, config='config.json') - with open('test.txt', 'w') as f: - f.write('Test line 1\nTest line 2\nTest line 3\n') - main() - with open('test.txt', 'r') as f: - lines = f.readlines() - self.assertEqual(lines, ['Replaced line 1\n', 'Replaced line 2\n', 'Replaced line 3\n']) - os.remove('test.txt') + for filename in args.files: + for replacement in replacements: + replace_in_file(filename, replacement['search'], replacement['replacement']) + else: + for filename in args.files: + replace_in_file(filename, args.find, args.replacement) - @patch('argparse.ArgumentParser.parse_args') - def test_main_with_direct_mode(self, mock_args): - mock_args.return_value = argparse.Namespace(files=[os.path.join(os.getcwd(), 'test.txt')], find='Test', replacement='Replaced', read_from_file=False, config=None) - with open('test.txt', 'w') as f: - f.write('Test line 1\nTest line 2\nTest line 3\n') - main() - with open('test.txt', 'r') as f: - lines = f.readlines() - self.assertEqual(lines, ['Replaced line 1\n', 'Replaced line 2\n', 'Replaced line 3\n']) - os.remove('test.txt') - -if __name__ == '__main__': - unittest.main() \ No newline at end of file +if __name__ == "__main__": + main() \ No newline at end of file From 3f25857c8f37db69eaebc2694b0aadc69f8280e4 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:00:29 -0300 Subject: [PATCH 04/61] feat/intial-v initial code --- .gitignore | 2 ++ README.md | 2 +- tests/__init__.py | 0 tests/test_main.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/__init__.py create mode 100644 tests/test_main.py diff --git a/.gitignore b/.gitignore index 82f9275..f716c20 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ __pycache__/ *.py[cod] *$py.class +.DS_Store + # C extensions *.so diff --git a/README.md b/README.md index 754abc3..0b688f7 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ In this configuration, the find-and-replace hook is set to read search and repla ## Run tests ``` -python3 -m unittest tests/test_main.py +python -m unittest tests.test_main ``` diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000..f37d7da --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,51 @@ +# import unittest +# from find_and_replace.main import replace_in_file, main +# class TestMainFunctions(unittest.TestCase): +# def test_replace_in_file(self): +# pass # TODO: Write your test here + +# def test_main(self): +# pass # TODO: Write your test here + +# if __name__ == '__main__': +# unittest.main() + +import unittest +import argparse +from unittest.mock import patch, mock_open +from find_and_replace.main import replace_in_file, main + +class TestMainFunctions(unittest.TestCase): + @patch('fileinput.FileInput') + def test_replace_in_file(self, mock_fileinput): + """ + This test checks if the replace_in_file function correctly opens the file and replaces the specified text. + """ + # Mock the file input to return a specific line of text + mock_fileinput.return_value.__enter__.return_value = ['hello world'] + # Call the function with a specific search and replacement + replace_in_file('dummy.txt', 'hello', 'hi') + # Assert that the file was opened correctly + mock_fileinput.assert_called_once_with('dummy.txt', inplace=True) + + @patch('argparse.ArgumentParser.parse_args') + @patch('find_and_replace.main.replace_in_file') + @patch('os.getcwd', return_value='/dummy/path') + @patch('builtins.open', new_callable=mock_open, read_data='{"search": "hello", "replacement": "hi"}') + @patch('json.load', return_value=[{"search": "hello", "replacement": "hi"}]) + def test_main(self, mock_json_load, mock_open, mock_getcwd, mock_replace_in_file, mock_parse_args): + """ + This test checks if the main function correctly reads the configuration file and calls the replace_in_file function with the correct arguments. + """ + # Mock the command line arguments + mock_parse_args.return_value = argparse.Namespace(files=['dummy.txt'], find=None, replacement=None, read_from_file=True, config='.find-and-replace.json') + # Call the main function + main() + # Assert that the config file was opened correctly and the replace_in_file function was called with the correct arguments + mock_open.assert_called_once_with('/dummy/path/.find-and-replace.json', 'r') + mock_replace_in_file.assert_called_once_with('dummy.txt', 'hello', 'hi') + +if __name__ == '__main__': + unittest.main() + + From 7a082ad8eaa61bdd6dd4b8f866968af9d05fac5f Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:17:51 -0300 Subject: [PATCH 05/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/publish-to-pypi.yml diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml new file mode 100644 index 0000000..831b5aa --- /dev/null +++ b/.github/workflows/publish-to-pypi.yml @@ -0,0 +1,37 @@ +name: Publish Python 🐍 distributions 📦 to PyPI + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build-n-publish: + name: Build and publish Python 🐍 distributions 📦 to PyPI + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v5.1.0 + with: + python-version: 3.11 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + + - name: Build a binary wheel and a source tarball + run: | + python setup.py sdist bdist_wheel + + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@master + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} From 818c9884f9dbcbe1a1c969c1d91a3c4fb0413ecb Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:22:37 -0300 Subject: [PATCH 06/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 831b5aa..fd232fd 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -11,7 +11,7 @@ on: jobs: build-n-publish: name: Build and publish Python 🐍 distributions 📦 to PyPI - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From 031365989b1a772d198be29df6590add776dceeb Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:26:26 -0300 Subject: [PATCH 07/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 2 +- setup.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index fd232fd..a0e18cb 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -31,7 +31,7 @@ jobs: python setup.py sdist bdist_wheel - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/setup.py b/setup.py index 6068493..bc9ab22 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,19 @@ +# from setuptools import setup + +# setup() + + from setuptools import setup -setup() +# read the contents of your README file +from pathlib import Path +this_directory = Path(__file__).parent +long_description = (this_directory / "README.md").read_text() + +setup( + name='your-package-name', + version='0.1', + description='Your package description', + long_description=long_description, + long_description_content_type='text/markdown' +) From 77a40c1f1e27a0b934de7898ce0acf23e3aa51af Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:28:41 -0300 Subject: [PATCH 08/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index a0e18cb..af7ce7a 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -12,6 +12,8 @@ jobs: build-n-publish: name: Build and publish Python 🐍 distributions 📦 to PyPI runs-on: ubuntu-latest + permissions: + id-token: write steps: - uses: actions/checkout@v2 From a60078626611120c43f6c83ecf8ff9009dd549b0 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:34:22 -0300 Subject: [PATCH 09/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 14 ++++---------- scripts/publish.sh | 4 +--- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index af7ce7a..1e10340 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -26,14 +26,8 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel twine - - name: Build a binary wheel and a source tarball - run: | - python setup.py sdist bdist_wheel - - - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + - name: Run publish script + run: scripts/publish.sh + env: + PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }} diff --git a/scripts/publish.sh b/scripts/publish.sh index 7797311..398e58a 100644 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -15,6 +15,4 @@ fi python3 setup.py sdist # Upload the package to PyPI -echo "Enter your PyPI API token:" -token=$token -twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p $token dist/* +twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p $PYPI_API_TOKEN dist/* \ No newline at end of file From e529f7defc441c16eea131935aad8c268732df3e Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:36:18 -0300 Subject: [PATCH 10/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 1e10340..6cb1823 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -27,6 +27,9 @@ jobs: run: | python -m pip install --upgrade pip + - name: Add execute permissions to script + run: chmod +x scripts/publish.sh + - name: Run publish script run: scripts/publish.sh env: From 122b15262be138ff790ab217f213df0308990eb7 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:37:21 -0300 Subject: [PATCH 11/61] feat/intial-v initial code --- setup.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/setup.py b/setup.py index bc9ab22..ecd37c5 100644 --- a/setup.py +++ b/setup.py @@ -1,19 +1,4 @@ -# from setuptools import setup - -# setup() - - from setuptools import setup -# read the contents of your README file -from pathlib import Path -this_directory = Path(__file__).parent -long_description = (this_directory / "README.md").read_text() +setup() -setup( - name='your-package-name', - version='0.1', - description='Your package description', - long_description=long_description, - long_description_content_type='text/markdown' -) From 18ba1852d29ed5b7a8293abe009da279055cba71 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:40:48 -0300 Subject: [PATCH 12/61] feat/intial-v initial code --- setup.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ecd37c5..84ca123 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,17 @@ +# from setuptools import setup + +# setup() + + from setuptools import setup -setup() +setup( + name="find-and-replace-template-commit-check", + version="0.8.0", + description="A tool to find and replace templates in commit messages", + packages=["find_and_replace_template_commit_check"], + author="Your Name", + author_email="your.email@example.com", + url="https://github.com/yourusername/find-and-replace-template-commit-check", +) From d108c0537571bc890b82ba12679a8827a083a613 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:47:07 -0300 Subject: [PATCH 13/61] feat/intial-v initial code --- setup.cfg | 4 ++-- setup.py | 25 ++++++++++++------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/setup.cfg b/setup.cfg index 0cbc552..6679860 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,8 +1,8 @@ # setup.cfg [metadata] -name = find-and-replace-template-commit-check +name = find-and-replace-commit-check description = Finds strings in files and replaces them with other strings. -version = 0.8.0 +version = 0.9.0 author = Andre Ouellet author_email = me@here.com license = MIT diff --git a/setup.py b/setup.py index 84ca123..692c829 100644 --- a/setup.py +++ b/setup.py @@ -1,17 +1,16 @@ -# from setuptools import setup +from setuptools import setup -# setup() +setup() -from setuptools import setup - -setup( - name="find-and-replace-template-commit-check", - version="0.8.0", - description="A tool to find and replace templates in commit messages", - packages=["find_and_replace_template_commit_check"], - author="Your Name", - author_email="your.email@example.com", - url="https://github.com/yourusername/find-and-replace-template-commit-check", -) +# from setuptools import setup +# setup( +# name="find-and-replace-template-commit-check", +# version="0.8.0", +# description="A tool to find and replace templates in commit messages", +# packages=["find_and_replace_template_commit_check"], +# author="Your Name", +# author_email="your.email@example.com", +# url="https://github.com/yourusername/find-and-replace-template-commit-check", +# ) \ No newline at end of file From 073a2275d64d3697f3dc5a743a45fe0655f12219 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:52:04 -0300 Subject: [PATCH 14/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 6cb1823..3367856 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -26,6 +26,13 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip + pip install build twine + + - name: Build package + run: python -m build + + - name: Publish package + run: twine upload dist/* - name: Add execute permissions to script run: chmod +x scripts/publish.sh From 1a43b34660f1ace637cb67387e05cb404f5fd07c Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:53:56 -0300 Subject: [PATCH 15/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 3367856..39090a8 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -30,14 +30,12 @@ jobs: - name: Build package run: python -m build - + - name: Publish package run: twine upload dist/* - name: Add execute permissions to script run: chmod +x scripts/publish.sh - - name: Run publish script - run: scripts/publish.sh - env: - PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + - name: Publish package + run: twine upload dist/* --non-interactive -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} From 7f2f56424695e9cec5f856decfd767d7a6aa7f80 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 18:56:16 -0300 Subject: [PATCH 16/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 39090a8..0696677 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -38,4 +38,6 @@ jobs: run: chmod +x scripts/publish.sh - name: Publish package - run: twine upload dist/* --non-interactive -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} + run: | + chmod +x scripts/publish.sh + twine upload dist/* --non-interactive -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} From 644caf2c7e33164cea21d8a028c873d50d5fe735 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 19:25:40 -0300 Subject: [PATCH 17/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 0696677..c2928c3 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -22,22 +22,20 @@ jobs: uses: actions/setup-python@v5.1.0 with: python-version: 3.11 - - name: Install dependencies run: | python -m pip install --upgrade pip - pip install build twine - - - name: Build package - run: python -m build - - - name: Publish package - run: twine upload dist/* - - - name: Add execute permissions to script - run: chmod +x scripts/publish.sh - - - name: Publish package - run: | - chmod +x scripts/publish.sh - twine upload dist/* --non-interactive -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} + if ! python -c "import twine" &> /dev/null; then + echo "twine not found, installing..." + pip install twine + fi + if ! python -c "import setuptools" &> /dev/null; then + echo "setuptools not found, installing..." + pip install setuptools + fi + + - name: Build the package + run: python setup.py sdist + + - name: Publish the package + run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} dist/* From 7353f8d12a1dea4358f92308b1172a837e9d961e Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 19:29:58 -0300 Subject: [PATCH 18/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index c2928c3..e0f848c 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -22,6 +22,7 @@ jobs: uses: actions/setup-python@v5.1.0 with: python-version: 3.11 + - name: Install dependencies run: | python -m pip install --upgrade pip @@ -37,5 +38,8 @@ jobs: - name: Build the package run: python setup.py sdist + - name: List contents of dist directory + run: ls dist + - name: Publish the package run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} dist/* From 6c9a6c614b1176ec20f01482cd9dc2d807995e3f Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 19:35:12 -0300 Subject: [PATCH 19/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index e0f848c..9127ebe 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -41,5 +41,8 @@ jobs: - name: List contents of dist directory run: ls dist + - name: List contents of dist directory + run: pwd + - name: Publish the package - run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} dist/* + run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} ${{ github.workspace }}/dist/* From da5eadf8d338a766d0ec60d3594b6e19e18a65e4 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 19:40:25 -0300 Subject: [PATCH 20/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 9127ebe..210ce26 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -45,4 +45,4 @@ jobs: run: pwd - name: Publish the package - run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} ${{ github.workspace }}/dist/* + run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_TOKEN }} ${{ github.workspace }}/dist/* From 6cc413aff5747c8fc642acbb90937124749b1b57 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 19:53:15 -0300 Subject: [PATCH 21/61] feat/intial-v initial code --- .github/workflows/publish-to-pypi.yml | 22 +++++----------------- setup.cfg | 2 +- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 210ce26..2191383 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -23,26 +23,14 @@ jobs: with: python-version: 3.11 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - if ! python -c "import twine" &> /dev/null; then - echo "twine not found, installing..." - pip install twine - fi - if ! python -c "import setuptools" &> /dev/null; then - echo "setuptools not found, installing..." - pip install setuptools - fi + - name: Install twine + run: pip install twine + + - name: install setuptools + run: pip install setuptools - name: Build the package run: python setup.py sdist - - name: List contents of dist directory - run: ls dist - - - name: List contents of dist directory - run: pwd - - name: Publish the package run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_TOKEN }} ${{ github.workspace }}/dist/* diff --git a/setup.cfg b/setup.cfg index 6679860..80f1d13 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,7 +2,7 @@ [metadata] name = find-and-replace-commit-check description = Finds strings in files and replaces them with other strings. -version = 0.9.0 +version = 0.9.1 author = Andre Ouellet author_email = me@here.com license = MIT From c69171b6f29ee7723efd3ec8566f8d20fb04e044 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 19:53:25 -0300 Subject: [PATCH 22/61] feat/intial-v initial code --- setup.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/setup.py b/setup.py index 692c829..fc1f76c 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,3 @@ from setuptools import setup -setup() - - -# from setuptools import setup - -# setup( -# name="find-and-replace-template-commit-check", -# version="0.8.0", -# description="A tool to find and replace templates in commit messages", -# packages=["find_and_replace_template_commit_check"], -# author="Your Name", -# author_email="your.email@example.com", -# url="https://github.com/yourusername/find-and-replace-template-commit-check", -# ) \ No newline at end of file +setup() \ No newline at end of file From f5900338173a3e32a317f57f6a7a552c7cb98435 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 21:06:16 -0300 Subject: [PATCH 23/61] feat/intial-v unit test --- .github/workflows/unit-tests.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/unit-tests.yml diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 0000000..51046d5 --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,27 @@ +name: Publish Python 🐍 distributions 📦 to PyPI + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build-n-publish: + name: Build and publish Python 🐍 distributions 📦 to PyPI + runs-on: ubuntu-latest + permissions: + id-token: write + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v5.1.0 + with: + python-version: 3.11 + + - name: Run tests + run: python3 -m unittest tests/test_main.py From 8469058b849d4494d12fe0b884d42092fe6a95c5 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 21:08:11 -0300 Subject: [PATCH 24/61] feat/intial-v unit test --- .github/workflows/unit-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 51046d5..37bd455 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,4 +1,4 @@ -name: Publish Python 🐍 distributions 📦 to PyPI +name: Unit tests on: push: @@ -9,7 +9,7 @@ on: - main jobs: - build-n-publish: + unit-tests: name: Build and publish Python 🐍 distributions 📦 to PyPI runs-on: ubuntu-latest permissions: From dd3ffd51bec5fffe0701650cfa9a58148ec0b8aa Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Wed, 12 Jun 2024 21:12:13 -0300 Subject: [PATCH 25/61] feat/intial-v unit test --- .github/workflows/publish-to-pypi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 2191383..fe67b2c 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -33,4 +33,4 @@ jobs: run: python setup.py sdist - name: Publish the package - run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_TOKEN }} ${{ github.workspace }}/dist/* + run: twine upload --skip-existing --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_TOKEN }} ${{ github.workspace }}/dist/* From 315b947b9a92d9fa6063925edae89d3cc0289dcc Mon Sep 17 00:00:00 2001 From: andreouellet Date: Thu, 13 Jun 2024 00:15:14 -0300 Subject: [PATCH 26/61] feat/initial-v-update-usability-and-readability-setup-cfg-metadata --- README.md | 14 +++++++--- {scripts => assets}/publish.sh | 0 {images => assets}/pypi-package.png | Bin find_and_replace/main.py | 40 ++++++++++++++++++++-------- images/.DS_Store | Bin 6148 -> 0 bytes setup.cfg | 40 ++++++++++++++++++++++------ 6 files changed, 72 insertions(+), 22 deletions(-) rename {scripts => assets}/publish.sh (100%) rename {images => assets}/pypi-package.png (100%) delete mode 100644 images/.DS_Store diff --git a/README.md b/README.md index 0b688f7..23a323b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# Find and Replace Template Commit Check +# find-and-replace -This Python package provides a pre-commit hook that finds strings in files and replaces them with other strings. +Python package and pre-commit-hook for finding and replacing string(s) in file(s). ## Installation This is an easy to use package which is already available here https://pypi.org/project/find-and-replace-template-commit-check/: -![package to use](./images/pypi-package.png "Title") +![package to use](./assets/pypi-package.png "Title") You can install the package via pip: @@ -103,3 +103,11 @@ To build and publish it to pypi run ``` bash scripts/publish.sh ``` + +## Reference Info + +* https://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table +* https://setuptools.pypa.io/en/latest/userguide/declarative_config.html +* https://packaging.python.org/guides/distributing-packages-using-setuptools/ +* https://autopilot-docs.readthedocs.io/en/latest/license_list.html +* https://pypi.org/classifiers/ diff --git a/scripts/publish.sh b/assets/publish.sh similarity index 100% rename from scripts/publish.sh rename to assets/publish.sh diff --git a/images/pypi-package.png b/assets/pypi-package.png similarity index 100% rename from images/pypi-package.png rename to assets/pypi-package.png diff --git a/find_and_replace/main.py b/find_and_replace/main.py index afee58f..c9bfa76 100755 --- a/find_and_replace/main.py +++ b/find_and_replace/main.py @@ -5,21 +5,41 @@ import json import sys + def replace_in_file(filename, search, replacement): with fileinput.FileInput(filename, inplace=True) as file: for line in file: print(line.replace(rf"{search}", rf"{replacement}"), end='') + def main(): - parser = argparse.ArgumentParser(description="This script performs search and replace operations on one or more files. It supports two modes of operation: Direct Mode and File Mode. In Direct Mode, you specify the search and replacement strings directly on the command line. In File Mode, the script reads the search and replacement strings from a JSON file.") - parser.add_argument('files', nargs='*', help='Files to perform search and replace') - parser.add_argument('--find', help='Text to find in files') - parser.add_argument('--replacement', help='Text to replace with in files') - parser.add_argument('--read-from-file', type=bool, default=True, help='Read search and replacement strings from file') - parser.add_argument('--config', default='.find-and-replace.json', help='Path to the config file') + parser = argparse.ArgumentParser( + description="""Perform find and replace operations on one or more target files. + By default, the script reads the search and replacement entries (strings) from a JSON file. + You can also specify the search and replacement strings directly as command line args by setting the + --find "search_string" and --replacement "replacement_string" argument options.""" + ) + parser.add_argument( + '--config', default='.find-and-replace.json', + help='PATH to JSON config file containing find and replacement entries' + ) + parser.add_argument( + '--find', dest='direct_mode', action='store_true', help='String to find in files' + ) + parser.add_argument( + '--replacement', dest='direct_mode', action='store_true', help='String to replace with in files' + ) + parser.add_argument( + 'files', nargs='*', help='File(s) on which to perform search and replace' + ) args = parser.parse_args() - if args.read_from_file: + if args.direct_mode: + # Arguments --find and --replacement have been specified - running in direct mode + for filename in args.files: + replace_in_file(filename, args.find, args.replacement) + else: + # Arguments --find and --replacement have not been specified - running in default config file mode try: with open(os.path.join(os.getcwd(), args.config), 'r') as f: replacements = json.load(f) @@ -33,9 +53,7 @@ def main(): for filename in args.files: for replacement in replacements: replace_in_file(filename, replacement['search'], replacement['replacement']) - else: - for filename in args.files: - replace_in_file(filename, args.find, args.replacement) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/images/.DS_Store b/images/.DS_Store deleted file mode 100644 index dfb978bceff6a6eac785f6c5231ce7fb2e3acd52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~y-veW426%OLnW4sjQ0fq@di;<24-FWT2O_kXcvUovhmn(`~xD2kUBA-W6Az< z9XnS!L!1l17BBagKnGw>cjDc{)cn5t#EvRsB%RMV!yb27+Np6+U{0S3%ej+Kgtfi?9AuX&Q>QB zi`|)TQ4Z^qic&xdj1@S}W#{w%ivG*_f83;%6p#Y{N&%ZKZ=3.6 packages = find: [options.entry_points] From c57f89f70ae92385ceb071eceb5a7fa6101d6a4b Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 09:39:33 -0300 Subject: [PATCH 27/61] feat/intial-v fix publishing --- assets/publish.sh | 2 +- find-and-replace.py | 3 +++ setup.cfg | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 find-and-replace.py diff --git a/assets/publish.sh b/assets/publish.sh index 398e58a..0c7249e 100644 --- a/assets/publish.sh +++ b/assets/publish.sh @@ -15,4 +15,4 @@ fi python3 setup.py sdist # Upload the package to PyPI -twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p $PYPI_API_TOKEN dist/* \ No newline at end of file +twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p $PYPI_TOKEN dist/* \ No newline at end of file diff --git a/find-and-replace.py b/find-and-replace.py new file mode 100644 index 0000000..1c18e6d --- /dev/null +++ b/find-and-replace.py @@ -0,0 +1,3 @@ +# find-and-replace.py + +VERSION = '0.1.0' \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 89a5166..092fc8e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = find-and-replace +name = find-and-replace-check description = Python package and pre-commit-hook for finding and replacing string(s) in file(s). version = attr: find-and-replace.VERSION author = OpenCEPK Open Cloud Engineering Platform Kit From 8427546b3d505cc1b11dc08f928415b1be610d97 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 09:48:14 -0300 Subject: [PATCH 28/61] feat/intial-v fix tests --- tests/test_main.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index f37d7da..2f13384 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -28,22 +28,22 @@ def test_replace_in_file(self, mock_fileinput): # Assert that the file was opened correctly mock_fileinput.assert_called_once_with('dummy.txt', inplace=True) - @patch('argparse.ArgumentParser.parse_args') - @patch('find_and_replace.main.replace_in_file') - @patch('os.getcwd', return_value='/dummy/path') - @patch('builtins.open', new_callable=mock_open, read_data='{"search": "hello", "replacement": "hi"}') - @patch('json.load', return_value=[{"search": "hello", "replacement": "hi"}]) - def test_main(self, mock_json_load, mock_open, mock_getcwd, mock_replace_in_file, mock_parse_args): - """ - This test checks if the main function correctly reads the configuration file and calls the replace_in_file function with the correct arguments. - """ - # Mock the command line arguments - mock_parse_args.return_value = argparse.Namespace(files=['dummy.txt'], find=None, replacement=None, read_from_file=True, config='.find-and-replace.json') - # Call the main function - main() - # Assert that the config file was opened correctly and the replace_in_file function was called with the correct arguments - mock_open.assert_called_once_with('/dummy/path/.find-and-replace.json', 'r') - mock_replace_in_file.assert_called_once_with('dummy.txt', 'hello', 'hi') +@patch('argparse.ArgumentParser.parse_args') +@patch('find_and_replace.main.replace_in_file') +@patch('os.getcwd', return_value='/dummy/path') +@patch('builtins.open', new_callable=mock_open, read_data='{"search": "hello", "replacement": "hi"}') +@patch('json.load', return_value=[{"search": "hello", "replacement": "hi"}]) +def test_main(self, mock_json_load, mock_open, mock_getcwd, mock_replace_in_file, mock_parse_args): + """ + This test checks if the main function correctly reads the configuration file and calls the replace_in_file function with the correct arguments. + """ + # Mock the command line arguments + mock_parse_args.return_value = argparse.Namespace(files=['dummy.txt'], find=None, replacement=None, direct_mode=False, config='.find-and-replace.json') + # Call the main function + main() + # Assert that the config file was opened correctly and the replace_in_file function was called with the correct arguments + mock_open.assert_called_once_with('/dummy/path/.find-and-replace.json', 'r') + mock_replace_in_file.assert_called_once_with('dummy.txt', 'hello', 'hi') if __name__ == '__main__': unittest.main() From 128f5b21aa3539453813cd43013a9fa876aa05fd Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 10:01:59 -0300 Subject: [PATCH 29/61] feat/intial-v update the hook --- .pre-commit-hooks.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .pre-commit-hooks.yaml diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml new file mode 100644 index 0000000..1ed29b1 --- /dev/null +++ b/.pre-commit-hooks.yaml @@ -0,0 +1,5 @@ +- id: find-and-replace + name: find-and-replace + description: Finds strings in files and replaces them with other strings. + entry: find-and-replace + language: python From 2edcfaa43d3a69d78c70ae5467e3c9828732c384 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 10:34:03 -0300 Subject: [PATCH 30/61] feat/intial-v update the hook --- README.md | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 23a323b..3c7a209 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,12 @@ pip install find-and-replace-template-commit-check To use this package, you need to add it to your pre-commit configuration file (.pre-commit-config.yaml). Here's an example: +For config mod + ``` repos: - - repo: https://github.com/andreouellet/pre-commit-find-and-replace-test (To be changed when repo is ready) - rev: test3 + - repo: https://github.com/opencepk/find-and-replace + rev: v0.0.1 hooks: - id: find-and-replace name: find-and-replace @@ -31,20 +33,38 @@ repos: pass_filenames: true exclude_types: - binary - args: ["--read-from-file", "true"] files: README.md verbose: true ``` +and for direct mode + +``` +repos: + - repo: https://github.com/opencepk/find-and-replace + rev: v0.0.1 + hooks: + - id: find-and-replace + name: find-and-replace + description: Find and replace strings + entry: find-and-replace + language: python + pass_filenames: true + exclude_types: + - binary + args: ["--find", "search_string", "--replacement", "replacement_string"] + files: README.md + verbose: true +``` + Please note you also have a choice of - files: '.*\.md$' +files: '.\*\.md$' or - files: . +files: . In this configuration, the find-and-replace hook is set to read search and replacement strings from a file (.project-properties.json by default which should be defined in the root of the project you want to use this package). You can also specify the search and replacement strings directly in the args field (which is not a suggested way). - ## Run tests ``` @@ -59,6 +79,11 @@ python -m unittest tests.test_main find-and-replace --config .find-and-replace.json README1.md README2.md ``` +also if you prefer to use a direct mod + +``` +find-and-replace-check --find "old_string" --replacement "new_string" README1.md README2.md +``` ## If you need more help with the flags and usage of them @@ -87,7 +112,6 @@ options: ``` - ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. @@ -106,8 +130,8 @@ bash scripts/publish.sh ## Reference Info -* https://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table -* https://setuptools.pypa.io/en/latest/userguide/declarative_config.html -* https://packaging.python.org/guides/distributing-packages-using-setuptools/ -* https://autopilot-docs.readthedocs.io/en/latest/license_list.html -* https://pypi.org/classifiers/ +- https://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table +- https://setuptools.pypa.io/en/latest/userguide/declarative_config.html +- https://packaging.python.org/guides/distributing-packages-using-setuptools/ +- https://autopilot-docs.readthedocs.io/en/latest/license_list.html +- https://pypi.org/classifiers/ From e305ae4fb7b693c334b51e90e8f3d70037e08a49 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 10:36:01 -0300 Subject: [PATCH 31/61] feat/intial-v update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c7a209..d6232a7 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ This project is licensed under the terms of the MIT license. To build and publish it to pypi run ``` -bash scripts/publish.sh +bash assets/publish.sh ``` ## Reference Info From b35548fef16cbc4ceb3e5c6447398242f205e4b9 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 11:31:18 -0300 Subject: [PATCH 32/61] feat/intial-v version update --- find-and-replace.py | 3 --- find_and_replace/__init__.py | 1 + setup.cfg | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 find-and-replace.py diff --git a/find-and-replace.py b/find-and-replace.py deleted file mode 100644 index 1c18e6d..0000000 --- a/find-and-replace.py +++ /dev/null @@ -1,3 +0,0 @@ -# find-and-replace.py - -VERSION = '0.1.0' \ No newline at end of file diff --git a/find_and_replace/__init__.py b/find_and_replace/__init__.py index e69de29..e358d9a 100644 --- a/find_and_replace/__init__.py +++ b/find_and_replace/__init__.py @@ -0,0 +1 @@ +VERSION = '0.2.0' \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 092fc8e..bb983d5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [metadata] name = find-and-replace-check description = Python package and pre-commit-hook for finding and replacing string(s) in file(s). -version = attr: find-and-replace.VERSION +version = attr: find_and_replace.VERSION author = OpenCEPK Open Cloud Engineering Platform Kit author_email = opencepk@gmail.com license = GPLv3 From 8559e60485fe1dcb3ca7ff85d0ef7829f54c2235 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 11:45:55 -0300 Subject: [PATCH 33/61] feat/intial-v update names --- .pre-commit-hooks.yaml | 6 +++--- README.md | 22 +++++++++++----------- setup.cfg | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 1ed29b1..362b29c 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,5 +1,5 @@ -- id: find-and-replace - name: find-and-replace +- id: find-and-replace-strings + name: find-and-replace-strings description: Finds strings in files and replaces them with other strings. - entry: find-and-replace + entry: find-and-replace-strings language: python diff --git a/README.md b/README.md index d6232a7..2447c92 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This is an easy to use package which is already available here https://pypi.org/ You can install the package via pip: ```bash -pip install find-and-replace-template-commit-check +pip install find-and-replace-strings ``` ## Usage @@ -25,10 +25,10 @@ repos: - repo: https://github.com/opencepk/find-and-replace rev: v0.0.1 hooks: - - id: find-and-replace - name: find-and-replace + - id: find-and-replace-strings + name: find-and-replace-strings description: Find and replace strings - entry: find-and-replace + entry: find-and-replace-strings language: python pass_filenames: true exclude_types: @@ -45,10 +45,10 @@ repos: - repo: https://github.com/opencepk/find-and-replace rev: v0.0.1 hooks: - - id: find-and-replace - name: find-and-replace + - id: find-and-replace-strings + name: find-and-replace-strings description: Find and replace strings - entry: find-and-replace + entry: find-and-replace-strings language: python pass_filenames: true exclude_types: @@ -75,21 +75,21 @@ python -m unittest tests.test_main ## How to run it using installed python package ``` - pip install find-and-replace-template-commit-check + pip install find-and-replace-strings find-and-replace --config .find-and-replace.json README1.md README2.md ``` also if you prefer to use a direct mod ``` -find-and-replace-check --find "old_string" --replacement "new_string" README1.md README2.md +find-and-replace-strings --find "old_string" --replacement "new_string" README1.md README2.md ``` ## If you need more help with the flags and usage of them ``` -find-and-replace -h -usage: find-and-replace [-h] [--search SEARCH] [--replacement REPLACEMENT] [--read-from-file READ_FROM_FILE] +find-and-replace-strings -h +usage: find-and-replace-strings [-h] [--search SEARCH] [--replacement REPLACEMENT] [--read-from-file READ_FROM_FILE] [--config REPLACEMENTS_FILE] [files ...] diff --git a/setup.cfg b/setup.cfg index bb983d5..cf5a992 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = find-and-replace-check +name = find-and-replace-strings description = Python package and pre-commit-hook for finding and replacing string(s) in file(s). version = attr: find_and_replace.VERSION author = OpenCEPK Open Cloud Engineering Platform Kit @@ -37,4 +37,4 @@ packages = find: [options.entry_points] console_scripts = - find-and-replace = find_and_replace.main:main + find-and-replace-strings = find_and_replace.main:main From 1ef5339012f7bfe58aa80b5830cc1781906ec8fd Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 15:41:34 -0300 Subject: [PATCH 34/61] test toml file --- find-and-replace.py | 3 --- pyproject.toml | 41 +++++++++++++++++++++++++++++++++++++++++ setup.cfg | 40 ---------------------------------------- setup.py | 17 +++++++++++++++-- 4 files changed, 56 insertions(+), 45 deletions(-) delete mode 100644 find-and-replace.py create mode 100644 pyproject.toml delete mode 100644 setup.cfg diff --git a/find-and-replace.py b/find-and-replace.py deleted file mode 100644 index 1c18e6d..0000000 --- a/find-and-replace.py +++ /dev/null @@ -1,3 +0,0 @@ -# find-and-replace.py - -VERSION = '0.1.0' \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..16d8f5c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,41 @@ +[build-system] +requires = ["setuptools", "wheel"] + +[project] +name = "find-and-replace-strings" +version = "1.0.1" +description = "Python package and pre-commit-hook for finding and replacing string(s) in file(s)." +license = { text = "GPLv3" } +authors = [{name = "OpenCEPK Open Cloud Engineering Platform Kit", email = "opencepk@gmail.com"}] +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Natural Language :: English", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Topic :: File Formats :: JSON", + "Topic :: Software Development :: Pre-processors", + "Topic :: Software Development :: Version Control :: Git", + "Topic :: Text Processing", + "Topic :: Text Processing :: Filters", + "Topic :: Text Processing :: General", + "Topic :: Utilities" +] + +keywords = ["find", "replace", "string", "file", "pre-commit", "hook", "git", "tool", "utility", "opencepk"] + + +[project.scripts] +find-and-replace-strings = "find_and_replace.main:main" + + diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 092fc8e..0000000 --- a/setup.cfg +++ /dev/null @@ -1,40 +0,0 @@ -[metadata] -name = find-and-replace-check -description = Python package and pre-commit-hook for finding and replacing string(s) in file(s). -version = attr: find-and-replace.VERSION -author = OpenCEPK Open Cloud Engineering Platform Kit -author_email = opencepk@gmail.com -license = GPLv3 -license_files = LICENSE -url = https://github.com/opencepk/find-and-replace -keywords = find, replace, string, file, pre-commit, hook, git, tool, utility, opencepk -classifiers = - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Development Status :: 4 - Beta - Intended Audience :: Developers - Natural Language :: English - License :: OSI Approved :: GNU General Public License v3 (GPLv3) - Topic :: File Formats :: JSON - Topic :: Software Development :: Pre-processors - Topic :: Software Development :: Version Control :: Git - Topic :: Text Processing - Topic :: Text Processing :: Filters - Topic :: Text Processing :: General - Topic :: Utilities - -[options] -python_requires = >=3.6 -packages = find: - -[options.entry_points] -console_scripts = - find-and-replace = find_and_replace.main:main diff --git a/setup.py b/setup.py index fc1f76c..e63c73a 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,16 @@ -from setuptools import setup +from setuptools import setup, find_packages -setup() \ No newline at end of file +setup( + name="find-and-replace-strings", + version="1.0.0", + description="Python package and pre-commit-hook for finding and replacing string(s) in file(s).", + author="OpenCEPK Open Cloud Engineering Platform Kit", + author_email="opencepk@gmail.com", + python_requires=">=3.7", + packages=find_packages(include=["find_and_replace", "find_and_replace.*"]), + entry_points={ + "console_scripts": [ + "find-and-replace-strings=find_and_replace.main:main", + ], + }, +) \ No newline at end of file From 84b93766e5a8a8c7c124d162eba9305a7cbb1713 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 16:02:16 -0300 Subject: [PATCH 35/61] add toml file --- find_and_replace/__init__.py | 1 - pyproject.toml | 40 ++++++++++++++++++++++++++++++++++++ setup.cfg | 40 ------------------------------------ setup.py | 17 +++++++++++++-- 4 files changed, 55 insertions(+), 43 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.cfg diff --git a/find_and_replace/__init__.py b/find_and_replace/__init__.py index e358d9a..e69de29 100644 --- a/find_and_replace/__init__.py +++ b/find_and_replace/__init__.py @@ -1 +0,0 @@ -VERSION = '0.2.0' \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0d6abd3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,40 @@ +[build-system] +requires = ["setuptools", "wheel"] + +[project] +name = "find-and-replace-strings" +version = "1.0.2" +description = "Python package and pre-commit-hook for finding and replacing string(s) in file(s)." +license = { text = "GPLv3" } +authors = [{name = "OpenCEPK Open Cloud Engineering Platform Kit", email = "opencepk@gmail.com"}] +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Natural Language :: English", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Topic :: File Formats :: JSON", + "Topic :: Software Development :: Pre-processors", + "Topic :: Software Development :: Version Control :: Git", + "Topic :: Text Processing", + "Topic :: Text Processing :: Filters", + "Topic :: Text Processing :: General", + "Topic :: Utilities" +] + +keywords = ["find", "replace", "string", "file", "pre-commit", "hook", "git", "tool", "utility", "opencepk"] + + +[project.scripts] +find-and-replace-strings = "find_and_replace.main:main" + diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index cf5a992..0000000 --- a/setup.cfg +++ /dev/null @@ -1,40 +0,0 @@ -[metadata] -name = find-and-replace-strings -description = Python package and pre-commit-hook for finding and replacing string(s) in file(s). -version = attr: find_and_replace.VERSION -author = OpenCEPK Open Cloud Engineering Platform Kit -author_email = opencepk@gmail.com -license = GPLv3 -license_files = LICENSE -url = https://github.com/opencepk/find-and-replace -keywords = find, replace, string, file, pre-commit, hook, git, tool, utility, opencepk -classifiers = - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Development Status :: 4 - Beta - Intended Audience :: Developers - Natural Language :: English - License :: OSI Approved :: GNU General Public License v3 (GPLv3) - Topic :: File Formats :: JSON - Topic :: Software Development :: Pre-processors - Topic :: Software Development :: Version Control :: Git - Topic :: Text Processing - Topic :: Text Processing :: Filters - Topic :: Text Processing :: General - Topic :: Utilities - -[options] -python_requires = >=3.6 -packages = find: - -[options.entry_points] -console_scripts = - find-and-replace-strings = find_and_replace.main:main diff --git a/setup.py b/setup.py index fc1f76c..e63c73a 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,16 @@ -from setuptools import setup +from setuptools import setup, find_packages -setup() \ No newline at end of file +setup( + name="find-and-replace-strings", + version="1.0.0", + description="Python package and pre-commit-hook for finding and replacing string(s) in file(s).", + author="OpenCEPK Open Cloud Engineering Platform Kit", + author_email="opencepk@gmail.com", + python_requires=">=3.7", + packages=find_packages(include=["find_and_replace", "find_and_replace.*"]), + entry_points={ + "console_scripts": [ + "find-and-replace-strings=find_and_replace.main:main", + ], + }, +) \ No newline at end of file From 4c8c282719eb0449f285316ab09a55863ad65739 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 17:16:49 -0300 Subject: [PATCH 36/61] fix exclusion --- .github/workflows/publish-to-pypi.yml | 6 ++++-- pyproject.toml | 20 ++++++++++++++++---- setup.py | 24 ++++++++++++------------ 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index fe67b2c..668784c 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -24,13 +24,15 @@ jobs: python-version: 3.11 - name: Install twine - run: pip install twine + run: pip install twine bui - name: install setuptools run: pip install setuptools - name: Build the package - run: python setup.py sdist + run: python -m build - name: Publish the package run: twine upload --skip-existing --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_TOKEN }} ${{ github.workspace }}/dist/* + + diff --git a/pyproject.toml b/pyproject.toml index 16d8f5c..f2a9ca7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,25 @@ [build-system] -requires = ["setuptools", "wheel"] +requires = ["setuptools>=61.0", "wheel"] + + + +build-backend = "setuptools.build_meta" + +[tool.setuptools] +packages = ["find_and_replace"] [project] name = "find-and-replace-strings" -version = "1.0.1" +version = "1.0.3" description = "Python package and pre-commit-hook for finding and replacing string(s) in file(s)." license = { text = "GPLv3" } authors = [{name = "OpenCEPK Open Cloud Engineering Platform Kit", email = "opencepk@gmail.com"}] -requires-python = ">=3.7" +dependencies = [ + 'tomli; python_version < "3.11"', +] +requires-python = ">=3.9" + + classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", @@ -36,6 +48,6 @@ keywords = ["find", "replace", "string", "file", "pre-commit", "hook", "git", "t [project.scripts] -find-and-replace-strings = "find_and_replace.main:main" +find-and-replace-strings = "find_and_replace.__main__:main" diff --git a/setup.py b/setup.py index e63c73a..30a40ea 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,16 @@ from setuptools import setup, find_packages setup( - name="find-and-replace-strings", - version="1.0.0", - description="Python package and pre-commit-hook for finding and replacing string(s) in file(s).", - author="OpenCEPK Open Cloud Engineering Platform Kit", - author_email="opencepk@gmail.com", - python_requires=">=3.7", - packages=find_packages(include=["find_and_replace", "find_and_replace.*"]), - entry_points={ - "console_scripts": [ - "find-and-replace-strings=find_and_replace.main:main", - ], - }, + # name="find-and-replace-strings", + # version="1.0.0", + # description="Python package and pre-commit-hook for finding and replacing string(s) in file(s).", + # author="OpenCEPK Open Cloud Engineering Platform Kit", + # author_email="opencepk@gmail.com", + # python_requires=">=3.7", + # packages=find_packages(include=["find_and_replace", "find_and_replace.*"]), + # entry_points={ + # "console_scripts": [ + # "find-and-replace-strings=find_and_replace.main:main", + # ], + # }, ) \ No newline at end of file From 24ac2557222625837688c53e4871d0573d8cc4b1 Mon Sep 17 00:00:00 2001 From: andreouellet Date: Thu, 13 Jun 2024 17:22:47 -0300 Subject: [PATCH 37/61] feat/intial-v-update-publish-pypi-ghactions-only-run-on-merged-pr --- .github/workflows/publish-to-pypi.yml | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 668784c..0adc108 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -1,15 +1,14 @@ name: Publish Python 🐍 distributions 📦 to PyPI on: - push: - branches: - - main - pull_request: - branches: - - main + pull_request: + branches: + - main + types: [closed] jobs: build-n-publish: + if: ${{ github.event.pull_request.merged }} name: Build and publish Python 🐍 distributions 📦 to PyPI runs-on: ubuntu-latest permissions: @@ -23,16 +22,11 @@ jobs: with: python-version: 3.11 - - name: Install twine - run: pip install twine bui - - - name: install setuptools - run: pip install setuptools + - name: Install pip packages + run: pip install twine build setuptools - name: Build the package run: python -m build - name: Publish the package - run: twine upload --skip-existing --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_TOKEN }} ${{ github.workspace }}/dist/* - - + run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_TOKEN }} ${{ github.workspace }}/dist/* From e06bf971329bdce5a0500475f128ba8676679b30 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 17:49:51 -0300 Subject: [PATCH 38/61] feat/intial-v rename folder --- .../__init__.py | 0 {find_and_replace => find_and_replace_strings}/main.py | 0 pyproject.toml | 8 ++------ 3 files changed, 2 insertions(+), 6 deletions(-) rename {find_and_replace => find_and_replace_strings}/__init__.py (100%) rename {find_and_replace => find_and_replace_strings}/main.py (100%) diff --git a/find_and_replace/__init__.py b/find_and_replace_strings/__init__.py similarity index 100% rename from find_and_replace/__init__.py rename to find_and_replace_strings/__init__.py diff --git a/find_and_replace/main.py b/find_and_replace_strings/main.py similarity index 100% rename from find_and_replace/main.py rename to find_and_replace_strings/main.py diff --git a/pyproject.toml b/pyproject.toml index ac94db6..45b9f60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [tool.setuptools] -packages = ["find_and_replace"] +packages = ["find_and_replace_strings"] [project] name = "find-and-replace-strings" @@ -48,10 +48,6 @@ keywords = ["find", "replace", "string", "file", "pre-commit", "hook", "git", "t [project.scripts] -<<<<<<< HEAD -find-and-replace-strings = "find_and_replace.main:main" -======= -find-and-replace-strings = "find_and_replace.__main__:main" +find-and-replace-strings = "find_and_replace_strings.__main__:main" ->>>>>>> test From d6fb84fec7702ec351bd3df2f4052398b90711ec Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 17:52:25 -0300 Subject: [PATCH 39/61] feat/intial-v clean up --- setup.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/setup.py b/setup.py index 30a40ea..175b6d1 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,3 @@ from setuptools import setup, find_packages -setup( - # name="find-and-replace-strings", - # version="1.0.0", - # description="Python package and pre-commit-hook for finding and replacing string(s) in file(s).", - # author="OpenCEPK Open Cloud Engineering Platform Kit", - # author_email="opencepk@gmail.com", - # python_requires=">=3.7", - # packages=find_packages(include=["find_and_replace", "find_and_replace.*"]), - # entry_points={ - # "console_scripts": [ - # "find-and-replace-strings=find_and_replace.main:main", - # ], - # }, -) \ No newline at end of file +setup() \ No newline at end of file From 67e3d567403827ccf257f92490673cf897359d23 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Thu, 13 Jun 2024 17:56:12 -0300 Subject: [PATCH 40/61] feat/intial-v clean up --- tests/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index 2f13384..04a0b52 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -13,7 +13,7 @@ import unittest import argparse from unittest.mock import patch, mock_open -from find_and_replace.main import replace_in_file, main +from find_and_replace_strings.main import replace_in_file, main class TestMainFunctions(unittest.TestCase): @patch('fileinput.FileInput') From faa5dd972fca99373551448e4cb35e36cebdd738 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 00:29:40 -0300 Subject: [PATCH 41/61] feat/intial-v add commit hook --- .pre-commit-config.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..3a03da3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +--- +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-merge-conflict + - id: check-added-large-files + args: [--maxkb=500] + - id: trailing-whitespace + - id: detect-private-key + - id: end-of-file-fixer + - id: fix-encoding-pragma + - id: file-contents-sorter + - id: check-case-conflict + - id: mixed-line-ending + args: [--fix=lf] + + - repo: https://github.com/zricethezav/gitleaks + rev: v8.18.1 + hooks: + - id: gitleaks From 1dcd36077332329120884de2f7abf22f27c58fd9 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 00:32:14 -0300 Subject: [PATCH 42/61] feat/intial-v add commit hook --- assets/publish.sh | 2 +- find_and_replace_strings/main.py | 2 +- pyproject.toml | 2 -- setup.py | 3 ++- tests/test_main.py | 3 +-- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/assets/publish.sh b/assets/publish.sh index 0c7249e..5133bd2 100644 --- a/assets/publish.sh +++ b/assets/publish.sh @@ -15,4 +15,4 @@ fi python3 setup.py sdist # Upload the package to PyPI -twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p $PYPI_TOKEN dist/* \ No newline at end of file +twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p $PYPI_TOKEN dist/* diff --git a/find_and_replace_strings/main.py b/find_and_replace_strings/main.py index c9bfa76..293545f 100755 --- a/find_and_replace_strings/main.py +++ b/find_and_replace_strings/main.py @@ -14,7 +14,7 @@ def replace_in_file(filename, search, replacement): def main(): parser = argparse.ArgumentParser( - description="""Perform find and replace operations on one or more target files. + description="""Perform find and replace operations on one or more target files. By default, the script reads the search and replacement entries (strings) from a JSON file. You can also specify the search and replacement strings directly as command line args by setting the --find "search_string" and --replacement "replacement_string" argument options.""" diff --git a/pyproject.toml b/pyproject.toml index 45b9f60..342360b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,5 +49,3 @@ keywords = ["find", "replace", "string", "file", "pre-commit", "hook", "git", "t [project.scripts] find-and-replace-strings = "find_and_replace_strings.__main__:main" - - diff --git a/setup.py b/setup.py index 175b6d1..977789b 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from setuptools import setup, find_packages -setup() \ No newline at end of file +setup() diff --git a/tests/test_main.py b/tests/test_main.py index 04a0b52..5f97894 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # import unittest # from find_and_replace.main import replace_in_file, main # class TestMainFunctions(unittest.TestCase): @@ -47,5 +48,3 @@ def test_main(self, mock_json_load, mock_open, mock_getcwd, mock_replace_in_file if __name__ == '__main__': unittest.main() - - From d5b19274d26e0a7bad0b05636316b46e0708aea8 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 09:53:52 -0300 Subject: [PATCH 43/61] feat/intial-v publishing the package --- .github/workflows/publish-to-pypi.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 0adc108..bca27cc 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -28,5 +28,7 @@ jobs: - name: Build the package run: python -m build - - name: Publish the package - run: twine upload --repository-url https://upload.pypi.org/legacy/ -u __token__ -p ${{ secrets.PYPI_TOKEN }} ${{ github.workspace }}/dist/* + - name: Publish release distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + skip-existing: true From 3165ed1cb143b01068ecf4564e22ab6e2f7e85e1 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 10:05:14 -0300 Subject: [PATCH 44/61] feat/intial-v add long desc to avoid issue in pyblishing --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 342360b..a2db6ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ packages = ["find_and_replace_strings"] name = "find-and-replace-strings" version = "1.0.3" description = "Python package and pre-commit-hook for finding and replacing string(s) in file(s)." +readme = "README.md" license = { text = "GPLv3" } authors = [{name = "OpenCEPK Open Cloud Engineering Platform Kit", email = "opencepk@gmail.com"}] dependencies = [ From 39bfbe8d5088b17e6df5a2e473a29cc4f1c4c419 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 10:10:58 -0300 Subject: [PATCH 45/61] feat/intial-v cleanup --- tests/test_main.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index 5f97894..b083730 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,16 +1,3 @@ -# -*- coding: utf-8 -*- -# import unittest -# from find_and_replace.main import replace_in_file, main -# class TestMainFunctions(unittest.TestCase): -# def test_replace_in_file(self): -# pass # TODO: Write your test here - -# def test_main(self): -# pass # TODO: Write your test here - -# if __name__ == '__main__': -# unittest.main() - import unittest import argparse from unittest.mock import patch, mock_open From 031aa9a55c948c90d36237e3b2ad98e78216addc Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 10:18:45 -0300 Subject: [PATCH 46/61] feat/intial-v cleanup --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f716c20..08ea990 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ __pycache__/ *$py.class .DS_Store +**/.DS_Store # C extensions *.so From c613c139a957369f2f715020e0a78acda8fa50a2 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 10:22:07 -0300 Subject: [PATCH 47/61] feat/intial-v cleanup --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 08ea990..eb0e8bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,14 @@ # Byte-compiled / optimized / DLL files __pycache__/ +**/__pycache__/ *.py[cod] *$py.class .DS_Store **/.DS_Store + + # C extensions *.so From 0407de38178fc0eb7b8519cbbe1a66b1c88f1535 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 11:06:21 -0300 Subject: [PATCH 48/61] feat/intial-v version hook --- .pre-commit-config.yaml | 8 ++++++++ assets/check_version.py | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 assets/check_version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3a03da3..21df87c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,3 +21,11 @@ repos: rev: v8.18.1 hooks: - id: gitleaks + + - repo: local + hooks: + - id: check-version + name: Check version + entry: python assets/check_version.py + language: system + files: pyproject.toml \ No newline at end of file diff --git a/assets/check_version.py b/assets/check_version.py new file mode 100644 index 0000000..c89fac1 --- /dev/null +++ b/assets/check_version.py @@ -0,0 +1,20 @@ +import toml +import sys +import requests + +def main(): + # Load the pyproject.toml file + data = toml.load(open("pyproject.toml")) + + # Get the current version + current_version = data["project"]["version"] + + # Check if the version is already published + response = requests.get(f"https://pypi.org/pypi/find-and-replace-strings/{current_version}/json") + + if response.status_code == 200: + print("This version is already published. Please bump the version in pyproject.toml.") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file From 9a98349f00c90139ac1fe2c62b71e9f5c877a438 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 12:00:07 -0300 Subject: [PATCH 49/61] feat/intial-v v bumped but not committed error --- .pre-commit-config.yaml | 2 +- assets/check_version.py | 11 ++++++++++- pyproject.toml | 2 +- tests/test_main.py | 1 + 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 21df87c..567c67b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,4 +28,4 @@ repos: name: Check version entry: python assets/check_version.py language: system - files: pyproject.toml \ No newline at end of file + files: pyproject.toml diff --git a/assets/check_version.py b/assets/check_version.py index c89fac1..4318c6a 100644 --- a/assets/check_version.py +++ b/assets/check_version.py @@ -1,6 +1,8 @@ +# -*- coding: utf-8 -*- import toml import sys import requests +import subprocess def main(): # Load the pyproject.toml file @@ -16,5 +18,12 @@ def main(): print("This version is already published. Please bump the version in pyproject.toml.") sys.exit(1) + # Check if pyproject.toml has been modified but not committed + modified_files = subprocess.check_output(["git", "diff", "--name-only"]).decode().splitlines() + + if "pyproject.toml" in modified_files: + print("The version in pyproject.toml has been changed but not committed. Please commit your changes.") + sys.exit(1) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/pyproject.toml b/pyproject.toml index a2db6ac..3235c71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ packages = ["find_and_replace_strings"] [project] name = "find-and-replace-strings" -version = "1.0.3" +version = "1.0.6" description = "Python package and pre-commit-hook for finding and replacing string(s) in file(s)." readme = "README.md" license = { text = "GPLv3" } diff --git a/tests/test_main.py b/tests/test_main.py index b083730..78ab455 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import unittest import argparse from unittest.mock import patch, mock_open From c1d2b74367199759146bfa0c01ea752da6f4dbf7 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 14:51:56 -0300 Subject: [PATCH 50/61] feat/intial-v update e2e --- .gitignore | 2 +- .pre-commit-config.yaml | 9 +++++++++ README.md | 9 +++++++++ e2e/.find-and-replace.json | 26 ++++++++++++++++++++++++++ e2e/check-commit-hook.sh | 25 +++++++++++++++++++++++++ e2e/precommit-e2e.test | 1 + 6 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 e2e/.find-and-replace.json create mode 100755 e2e/check-commit-hook.sh create mode 100644 e2e/precommit-e2e.test diff --git a/.gitignore b/.gitignore index eb0e8bd..f8288bf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ __pycache__/ .DS_Store **/.DS_Store - +**/precommit-e2e.test # C extensions *.so diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 567c67b..c04f73d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,3 +29,12 @@ repos: entry: python assets/check_version.py language: system files: pyproject.toml + + - repo: local + hooks: + - id: find-and-replace-strings + name: Find and replace strings + entry: e2e/check-commit-hook.sh + language: system + files: 'e2e/.*\.test$' + verbose: true diff --git a/README.md b/README.md index 2447c92..2279459 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ Python package and pre-commit-hook for finding and replacing string(s) in file(s). +## Prerequisite + +pre-commit install +pre-commit install -t pre-push + +The above will make sure precommit will be run automatically on push + + + ## Installation This is an easy to use package which is already available here https://pypi.org/project/find-and-replace-template-commit-check/: diff --git a/e2e/.find-and-replace.json b/e2e/.find-and-replace.json new file mode 100644 index 0000000..7d67924 --- /dev/null +++ b/e2e/.find-and-replace.json @@ -0,0 +1,26 @@ +[ + { + "search": "{{BUSINESS_UNIT}}", + "replacement": "examp\"lebu" + }, + { + "search": "{{PROJECT_NAME}}", + "replacement": "exampleproject" + }, + { + "search": "{{GITHUB_REPO_URL}}", + "replacement": "https://github.com/examplebu/exampleproject" + }, + { + "search": "{{PROJECT_DESCRIPTION_SLUG}}", + "replacement": "Example project used to demonstrate all aspects of a project development and deployment" + }, + { + "search": "tucowsinc/iaascloudenablement", + "replacement": "tucowsinc/example-github-team-name" + }, + { + "search": "{{PROJECT_CONTRIBUTORS}}", + "replacement": "* [Andre Ouellet](mailto:aouellet@tucowsinc.com)" + } +] diff --git a/e2e/check-commit-hook.sh b/e2e/check-commit-hook.sh new file mode 100755 index 0000000..d5d3fbd --- /dev/null +++ b/e2e/check-commit-hook.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Store the original content of the file +original_content=$(cat e2e/precommit-e2e.test) +echo "Original content: $original_content" + +# Run the hook +python find_and_replace_strings/main.py --config e2e/.find-and-replace.json e2e/precommit-e2e.test + +# Check if the expected changes have been made +content=$(cat e2e/precommit-e2e.test) +echo "Content after running the hook: $content" + +if [[ "$content" != "# exampleproject" ]]; then + # If the changes are not as expected, print the exit code and exit with a status code of 1 + echo "Exit code: 1" + exit 1 +fi + +# Restore the original content of the file +echo "$original_content" > e2e/precommit-e2e.test + +# If the changes are as expected, print the exit code and exit with a status code of 0 +echo "Exit code: 0" +exit 0 diff --git a/e2e/precommit-e2e.test b/e2e/precommit-e2e.test new file mode 100644 index 0000000..a228fbf --- /dev/null +++ b/e2e/precommit-e2e.test @@ -0,0 +1 @@ +# {{PROJECT_NAME}} From 857f4ae8c4761cc703fd593afd63712eb8f0759b Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Fri, 14 Jun 2024 14:57:52 -0300 Subject: [PATCH 51/61] feat/intial-v add e2e workflow --- .github/workflows/e2e.yaml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/e2e.yaml diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 0000000..c7ec260 --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,24 @@ +name: E2E + +on: + push: + branches: + - main + pull_request: + +jobs: + check-commit-hook: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.11 + + - name: Run check-commit-hook.sh + run: | + chmod +x e2e/check-commit-hook.sh + ./e2e/check-commit-hook.sh \ No newline at end of file From 1197e40d5609793f2f7507c78a88766af71fadf2 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Mon, 17 Jun 2024 10:03:36 -0300 Subject: [PATCH 52/61] feat/intial-v update the package for pip --- find_and_replace_strings/__main__.py | 4 ++++ pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 find_and_replace_strings/__main__.py diff --git a/find_and_replace_strings/__main__.py b/find_and_replace_strings/__main__.py new file mode 100644 index 0000000..b727ca6 --- /dev/null +++ b/find_and_replace_strings/__main__.py @@ -0,0 +1,4 @@ +from .main import main + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3235c71..acbd4cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ packages = ["find_and_replace_strings"] [project] name = "find-and-replace-strings" -version = "1.0.6" +version = "1.0.7" description = "Python package and pre-commit-hook for finding and replacing string(s) in file(s)." readme = "README.md" license = { text = "GPLv3" } From df1fbd1bead75269d4262f42029568cca9d26dd1 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Mon, 17 Jun 2024 10:05:36 -0300 Subject: [PATCH 53/61] feat/intial-v update the package for pip --- .github/workflows/publish-to-pypi.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index bca27cc..b374fdd 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -4,11 +4,9 @@ on: pull_request: branches: - main - types: [closed] jobs: build-n-publish: - if: ${{ github.event.pull_request.merged }} name: Build and publish Python 🐍 distributions 📦 to PyPI runs-on: ubuntu-latest permissions: From cdcdd6761a471b8758cc49c603af888eda8377aa Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Mon, 17 Jun 2024 12:48:54 -0300 Subject: [PATCH 54/61] feat/intial-v update tomb --- .github/workflows/e2e.yaml | 2 +- .github/workflows/publish-to-pypi.yml | 2 ++ README.md | 39 +++++++++++++-------------- find_and_replace_strings/__main__.py | 3 ++- pyproject.toml | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index c7ec260..b39655e 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -21,4 +21,4 @@ jobs: - name: Run check-commit-hook.sh run: | chmod +x e2e/check-commit-hook.sh - ./e2e/check-commit-hook.sh \ No newline at end of file + ./e2e/check-commit-hook.sh diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index b374fdd..bca27cc 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -4,9 +4,11 @@ on: pull_request: branches: - main + types: [closed] jobs: build-n-publish: + if: ${{ github.event.pull_request.merged }} name: Build and publish Python 🐍 distributions 📦 to PyPI runs-on: ubuntu-latest permissions: diff --git a/README.md b/README.md index 2279459..3f3dc6b 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,7 @@ pre-commit install -t pre-push The above will make sure precommit will be run automatically on push - - -## Installation +## Installation as a pip package This is an easy to use package which is already available here https://pypi.org/project/find-and-replace-template-commit-check/: @@ -22,8 +20,13 @@ You can install the package via pip: ```bash pip install find-and-replace-strings ``` +In case if you want to use it from the root folder in source: + +``` + python -m find_and_replace_strings -h +``` -## Usage +## Usage as a pre commit hook To use this package, you need to add it to your pre-commit configuration file (.pre-commit-config.yaml). Here's an example: @@ -68,7 +71,7 @@ repos: ``` Please note you also have a choice of -files: '.\*\.md$' +files: '.*\.md$' or files: . @@ -98,26 +101,22 @@ find-and-replace-strings --find "old_string" --replacement "new_string" README1 ``` find-and-replace-strings -h -usage: find-and-replace-strings [-h] [--search SEARCH] [--replacement REPLACEMENT] [--read-from-file READ_FROM_FILE] - [--config REPLACEMENTS_FILE] - [files ...] +usage: find-and-replace-strings [-h] [--config CONFIG] [--find] [--replacement] + [files ...] -This script performs search and replace operations on one or more files. It supports two modes of operation: Direct Mode and -File Mode. In Direct Mode, you specify the search and replacement strings directly on the command line. In File Mode, the script -reads the search and replacement strings from a JSON file. +Perform find and replace operations on one or more target files. By default, the script +reads the search and replacement entries (strings) from a JSON file. You can also +specify the search and replacement strings directly as command line args by setting the +--find "search_string" and --replacement "replacement_string" argument options. positional arguments: - files Files to perform search and replace + files File(s) on which to perform search and replace options: - -h, --help show this help message and exit - --search SEARCH Text to search for - --replacement REPLACEMENT - Text to replace with - --read-from-file READ_FROM_FILE - Read search and replacement strings from file - --config REPLACEMENTS_FILE - Path to the replacements file + -h, --help show this help message and exit + --config CONFIG PATH to JSON config file containing find and replacement entries + --find String to find in files + --replacement String to replace with in files ``` diff --git a/find_and_replace_strings/__main__.py b/find_and_replace_strings/__main__.py index b727ca6..97aede6 100644 --- a/find_and_replace_strings/__main__.py +++ b/find_and_replace_strings/__main__.py @@ -1,4 +1,5 @@ +# -*- coding: utf-8 -*- from .main import main if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/pyproject.toml b/pyproject.toml index acbd4cd..77577ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ packages = ["find_and_replace_strings"] [project] name = "find-and-replace-strings" -version = "1.0.7" +version = "1.0.8" description = "Python package and pre-commit-hook for finding and replacing string(s) in file(s)." readme = "README.md" license = { text = "GPLv3" } From 905c7245d5817405ce6cb5406f7fb5deca3ee023 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Tue, 18 Jun 2024 10:06:11 -0300 Subject: [PATCH 55/61] feat/intial-v update the git flow --- .github/workflows/publish-to-pypi.yml | 4 +- find_and_replace_strings/main.py | 76 +++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index bca27cc..fa4718a 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -4,11 +4,11 @@ on: pull_request: branches: - main - types: [closed] + # types: [closed] jobs: build-n-publish: - if: ${{ github.event.pull_request.merged }} + # if: ${{ github.event.pull_request.merged }} name: Build and publish Python 🐍 distributions 📦 to PyPI runs-on: ubuntu-latest permissions: diff --git a/find_and_replace_strings/main.py b/find_and_replace_strings/main.py index 293545f..854abed 100755 --- a/find_and_replace_strings/main.py +++ b/find_and_replace_strings/main.py @@ -1,3 +1,63 @@ +# -*- coding: utf-8 -*- +# import os +# import argparse +# import fileinput +# import json +# import sys + + +# def replace_in_file(filename, search, replacement): +# with fileinput.FileInput(filename, inplace=True) as file: +# for line in file: +# print(line.replace(rf"{search}", rf"{replacement}"), end='') + + +# def main(): +# parser = argparse.ArgumentParser( +# description="""Perform find and replace operations on one or more target files. +# By default, the script reads the search and replacement entries (strings) from a JSON file. +# You can also specify the search and replacement strings directly as command line args by setting the +# --find "search_string" and --replacement "replacement_string" argument options.""" +# ) +# parser.add_argument( +# '--config', default='.find-and-replace.json', +# help='PATH to JSON config file containing find and replacement entries' +# ) +# parser.add_argument( +# '--find', dest='direct_mode', action='store_true', help='String to find in files' +# ) +# parser.add_argument( +# '--replacement', dest='direct_mode', action='store_true', help='String to replace with in files' +# ) +# parser.add_argument( +# 'files', nargs='*', help='File(s) on which to perform search and replace' +# ) +# args = parser.parse_args() + +# if args.direct_mode: +# # Arguments --find and --replacement have been specified - running in direct mode +# for filename in args.files: +# replace_in_file(filename, args.find, args.replacement) +# else: +# # Arguments --find and --replacement have not been specified - running in default config file mode +# try: +# with open(os.path.join(os.getcwd(), args.config), 'r') as f: +# replacements = json.load(f) +# except FileNotFoundError: +# print(f"Error: {args.config} file not found.") +# sys.exit(1) +# except json.JSONDecodeError: +# print(f"Error: {args.config} is not a valid JSON file.") +# sys.exit(1) + +# for filename in args.files: +# for replacement in replacements: +# replace_in_file(filename, replacement['search'], replacement['replacement']) + + +# if __name__ == "__main__": +# main() + # -*- coding: utf-8 -*- import os import argparse @@ -6,10 +66,13 @@ import sys -def replace_in_file(filename, search, replacement): - with fileinput.FileInput(filename, inplace=True) as file: +def replace_in_file(filename, search, replacement, dry_run): + with fileinput.FileInput(filename, inplace=not dry_run) as file: for line in file: - print(line.replace(rf"{search}", rf"{replacement}"), end='') + if search in line and dry_run: + print(f"{search} would be replaced with {replacement} in {filename}") + elif not dry_run: + print(line.replace(rf"{search}", rf"{replacement}"), end='') def main(): @@ -32,12 +95,15 @@ def main(): parser.add_argument( 'files', nargs='*', help='File(s) on which to perform search and replace' ) + parser.add_argument( + '--dry-run', action='store_true', help='Perform a dry run without making any changes' + ) args = parser.parse_args() if args.direct_mode: # Arguments --find and --replacement have been specified - running in direct mode for filename in args.files: - replace_in_file(filename, args.find, args.replacement) + replace_in_file(filename, args.find, args.replacement, args.dry_run) else: # Arguments --find and --replacement have not been specified - running in default config file mode try: @@ -52,7 +118,7 @@ def main(): for filename in args.files: for replacement in replacements: - replace_in_file(filename, replacement['search'], replacement['replacement']) + replace_in_file(filename, replacement['search'], replacement['replacement'], args.dry_run) if __name__ == "__main__": From a96f777dc62b39fb63e31787354ccd18ee1c1cdf Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Tue, 18 Jun 2024 10:10:23 -0300 Subject: [PATCH 56/61] feat/intial-v fix the test --- find_and_replace_strings/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/find_and_replace_strings/main.py b/find_and_replace_strings/main.py index 854abed..65b43d4 100755 --- a/find_and_replace_strings/main.py +++ b/find_and_replace_strings/main.py @@ -66,7 +66,7 @@ import sys -def replace_in_file(filename, search, replacement, dry_run): +def replace_in_file(filename, search, replacement, dry_run=False): with fileinput.FileInput(filename, inplace=not dry_run) as file: for line in file: if search in line and dry_run: From 3521521891179a4a32d6d62312a18c4e5a1e6c81 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Tue, 18 Jun 2024 10:11:23 -0300 Subject: [PATCH 57/61] feat/intial-v fix the test --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 77577ad..da91551 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ packages = ["find_and_replace_strings"] [project] name = "find-and-replace-strings" -version = "1.0.8" +version = "1.0.9" description = "Python package and pre-commit-hook for finding and replacing string(s) in file(s)." readme = "README.md" license = { text = "GPLv3" } From fe65bc79da451478c947b79f86922756706a12c5 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Tue, 18 Jun 2024 15:23:30 -0300 Subject: [PATCH 58/61] feat/intial-v extra features --- .pre-commit-config.yaml | 9 +++ README.md | 97 ++++++++++++++++++++++------- assets/pypi-package.png | Bin 93287 -> 70767 bytes find_and_replace_strings/main.py | 101 +++++++++++-------------------- 4 files changed, 121 insertions(+), 86 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c04f73d..a7ef568 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,3 +38,12 @@ repos: language: system files: 'e2e/.*\.test$' verbose: true + + - repo: local + hooks: + - id: unittest + name: Run unit tests + entry: python -m unittest tests.test_main + language: system + pass_filenames: false + always_run: true diff --git a/README.md b/README.md index 3f3dc6b..faa9a47 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,23 @@ files: . In this configuration, the find-and-replace hook is set to read search and replacement strings from a file (.project-properties.json by default which should be defined in the root of the project you want to use this package). You can also specify the search and replacement strings directly in the args field (which is not a suggested way). +## Usage as a python package +python -m find_and_replace_strings --usage +or +find-and-replace-strings --usage + +shows some usage examples. +``` + python -m find_and_replace_strings --usage +Example usages: +python -m find_and_replace_strings --config e2e/.find-and-replace.json e2e/precommit-e2e.test --dry-run --verbose +python -m find_and_replace_strings --config e2e/.find-and-replace.json e2e/precommit-e2e.test --dry-run --log-level=DEBUG +python -m find_and_replace_strings --find 'old_string' --replacement 'new_string' example.txt --verbose +python -m find_and_replace_strings --find 'old_string' --replacement 'new_string' example1.txt example2.txt --verbose +python -m find_and_replace_strings --config my_config.json example.txt --dry-run --verbose +python -m find_and_replace_strings --config e2e/.find-and-replace.json e2e/precommit-e2e.test --dry-run --log-level=INFO +``` + ## Run tests ``` @@ -88,7 +105,7 @@ python -m unittest tests.test_main ``` pip install find-and-replace-strings - find-and-replace --config .find-and-replace.json README1.md README2.md + find-and-replace --config .find-and-replace.json README1.md README2.md ``` also if you prefer to use a direct mod @@ -97,29 +114,74 @@ also if you prefer to use a direct mod find-and-replace-strings --find "old_string" --replacement "new_string" README1.md README2.md ``` -## If you need more help with the flags and usage of them +## Dry run + +Inside the project + +python -m find_and_replace_strings --config ./.find-and-replace.json ./README.md READMEtest.md --dry-run + +or using the deployed package + +find-and-replace-strings --config ./.find-and-replace.json ./README.md READ +MEtest.md --dry-run + +More example: + + +python -m find_and_replace_strings --config e2e/.find-and-replace.json e2e/precommit-e2e.test --dry-run --verbose + +python -m find_and_replace_strings --config e2e/.find-and-replace.json e2e/precommit-e2e.test --dry-run --log-level=DEBUG + +``` +python -m find_and_replace_strings --config e2e/.find-and-replace.json e2e/precommit-e2e.test --dry-run --verbose +INFO:root:Running in default config file mode +INFO:root:Replacing {{BUSINESS_UNIT}} with examp"lebu in e2e/precommit-e2e.test +INFO:root:Replacing {{PROJECT_NAME}} with exampleproject in e2e/precommit-e2e.test +INFO:root:{{PROJECT_NAME}} would be replaced with exampleproject in e2e/precommit-e2e.test +INFO:root:Replacing {{GITHUB_REPO_URL}} with https://github.com/examplebu/exampleproject in e2e/precommit-e2e.test +INFO:root:Replacing {{PROJECT_DESCRIPTION_SLUG}} with Example project used to demonstrate all aspects of a project development and deployment in e2e/precommit-e2e.test +INFO:root:Replacing tucowsinc/iaascloudenablement with tucowsinc/example-github-team-name in e2e/precommit-e2e.test +INFO:root:Replacing {{PROJECT_CONTRIBUTORS}} with * [Andre Ouellet](mailto:aouellet@tucowsinc.com) in e2e/precommit-e2e.test +``` + +## If you need more help with the available flags ``` -find-and-replace-strings -h -usage: find-and-replace-strings [-h] [--config CONFIG] [--find] [--replacement] - [files ...] +python -m find_and_replace_strings -h +usage: __main__.py [-h] [--config CONFIG] [--find] [--replacement] [--dry-run] + [--log-level LOG_LEVEL] [--verbose] [--usage] + [files ...] -Perform find and replace operations on one or more target files. By default, the script -reads the search and replacement entries (strings) from a JSON file. You can also -specify the search and replacement strings directly as command line args by setting the ---find "search_string" and --replacement "replacement_string" argument options. +Perform find and replace operations on one or more target files. By default, the script reads the +search and replacement entries (strings) from a JSON file. You can also specify the search and +replacement strings directly as command line args by setting the --find "search_string" and +--replacement "replacement_string" argument options. positional arguments: - files File(s) on which to perform search and replace + files File(s) on which to perform search and replace options: - -h, --help show this help message and exit - --config CONFIG PATH to JSON config file containing find and replacement entries - --find String to find in files - --replacement String to replace with in files + -h, --help show this help message and exit + --config CONFIG PATH to JSON config file containing find and replacement entries + --find String to find in files + --replacement String to replace with in files + --dry-run Perform a dry run without making any changes + --log-level LOG_LEVEL + Set the logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) + --verbose Print debug and info messages + --usage Print example usages ``` +## Building and Publishing + +To build and publish it to pypi run with proper token + +``` +bash assets/publish.sh +``` +or create a PR and after merge the changes will be published to the artifactory. + ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. @@ -128,13 +190,6 @@ Contributions are welcome! Please feel free to submit a Pull Request. This project is licensed under the terms of the MIT license. -## Building and Publishing - -To build and publish it to pypi run - -``` -bash assets/publish.sh -``` ## Reference Info diff --git a/assets/pypi-package.png b/assets/pypi-package.png index e8fcbb9300ba3551ab0745c579ca24a3401c0464..7e97d251156825fa007b960f124ad68d4afa6ca2 100644 GIT binary patch literal 70767 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

#iDdN2vPi6LH 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 z%0A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 NeM9GZ&ei*Y{|BFgXj1?H literal 93287 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 diff --git a/find_and_replace_strings/main.py b/find_and_replace_strings/main.py index 65b43d4..6b6d38d 100755 --- a/find_and_replace_strings/main.py +++ b/find_and_replace_strings/main.py @@ -1,80 +1,32 @@ -# -*- coding: utf-8 -*- -# import os -# import argparse -# import fileinput -# import json -# import sys - - -# def replace_in_file(filename, search, replacement): -# with fileinput.FileInput(filename, inplace=True) as file: -# for line in file: -# print(line.replace(rf"{search}", rf"{replacement}"), end='') - - -# def main(): -# parser = argparse.ArgumentParser( -# description="""Perform find and replace operations on one or more target files. -# By default, the script reads the search and replacement entries (strings) from a JSON file. -# You can also specify the search and replacement strings directly as command line args by setting the -# --find "search_string" and --replacement "replacement_string" argument options.""" -# ) -# parser.add_argument( -# '--config', default='.find-and-replace.json', -# help='PATH to JSON config file containing find and replacement entries' -# ) -# parser.add_argument( -# '--find', dest='direct_mode', action='store_true', help='String to find in files' -# ) -# parser.add_argument( -# '--replacement', dest='direct_mode', action='store_true', help='String to replace with in files' -# ) -# parser.add_argument( -# 'files', nargs='*', help='File(s) on which to perform search and replace' -# ) -# args = parser.parse_args() - -# if args.direct_mode: -# # Arguments --find and --replacement have been specified - running in direct mode -# for filename in args.files: -# replace_in_file(filename, args.find, args.replacement) -# else: -# # Arguments --find and --replacement have not been specified - running in default config file mode -# try: -# with open(os.path.join(os.getcwd(), args.config), 'r') as f: -# replacements = json.load(f) -# except FileNotFoundError: -# print(f"Error: {args.config} file not found.") -# sys.exit(1) -# except json.JSONDecodeError: -# print(f"Error: {args.config} is not a valid JSON file.") -# sys.exit(1) - -# for filename in args.files: -# for replacement in replacements: -# replace_in_file(filename, replacement['search'], replacement['replacement']) - - -# if __name__ == "__main__": -# main() - # -*- coding: utf-8 -*- import os import argparse import fileinput import json import sys +import logging def replace_in_file(filename, search, replacement, dry_run=False): + logging.info(f"Replacing {search} with {replacement} in {filename}") with fileinput.FileInput(filename, inplace=not dry_run) as file: for line in file: if search in line and dry_run: - print(f"{search} would be replaced with {replacement} in {filename}") + logging.info(f"{search} would be replaced with {replacement} in {filename}") elif not dry_run: print(line.replace(rf"{search}", rf"{replacement}"), end='') +def print_usage(): + print("Example usages:") + print("python -m find_and_replace_strings --config e2e/.find-and-replace.json e2e/precommit-e2e.test --dry-run --verbose") + print("python -m find_and_replace_strings --config e2e/.find-and-replace.json e2e/precommit-e2e.test --dry-run --log-level=DEBUG") + print("python -m find_and_replace_strings --find 'old_string' --replacement 'new_string' example.txt --verbose") + print("python -m find_and_replace_strings --find 'old_string' --replacement 'new_string' example1.txt example2.txt --verbose") + print("python -m find_and_replace_strings --config my_config.json example.txt --dry-run --verbose") + print("python -m find_and_replace_strings --config e2e/.find-and-replace.json e2e/precommit-e2e.test --dry-run --log-level=INFO") + + def main(): parser = argparse.ArgumentParser( description="""Perform find and replace operations on one or more target files. @@ -98,22 +50,41 @@ def main(): parser.add_argument( '--dry-run', action='store_true', help='Perform a dry run without making any changes' ) + parser.add_argument( + '--log-level', default='WARNING', help='Set the logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)' + ) + parser.add_argument( + '--verbose', action='store_true', help='Print debug and info messages' + ) + parser.add_argument( + '--usage', action='store_true', help='Print example usages' + ) args = parser.parse_args() + if args.usage: + print_usage() + sys.exit(0) + + levels = {'DEBUG': logging.DEBUG, 'INFO': logging.INFO, 'WARNING': logging.WARNING, 'ERROR': logging.ERROR, 'CRITICAL': logging.CRITICAL} + level = levels.get(args.log_level.upper(), logging.WARNING) + if args.verbose: + level = logging.DEBUG + logging.basicConfig(level=level) + if args.direct_mode: - # Arguments --find and --replacement have been specified - running in direct mode + logging.info("Running in direct mode") for filename in args.files: replace_in_file(filename, args.find, args.replacement, args.dry_run) else: - # Arguments --find and --replacement have not been specified - running in default config file mode + logging.info("Running in default config file mode") try: with open(os.path.join(os.getcwd(), args.config), 'r') as f: replacements = json.load(f) except FileNotFoundError: - print(f"Error: {args.config} file not found.") + logging.error(f"Error: {args.config} file not found.") sys.exit(1) except json.JSONDecodeError: - print(f"Error: {args.config} is not a valid JSON file.") + logging.error(f"Error: {args.config} is not a valid JSON file.") sys.exit(1) for filename in args.files: From 9b47f1524dc9878e0fa9a1626af9a64bd2ef9db3 Mon Sep 17 00:00:00 2001 From: hminaee-tc Date: Tue, 18 Jun 2024 16:43:39 -0300 Subject: [PATCH 59/61] feat/intial-v clean up --- .github/workflows/publish-to-pypi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index fa4718a..bca27cc 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -4,11 +4,11 @@ on: pull_request: branches: - main - # types: [closed] + types: [closed] jobs: build-n-publish: - # if: ${{ github.event.pull_request.merged }} + if: ${{ github.event.pull_request.merged }} name: Build and publish Python 🐍 distributions 📦 to PyPI runs-on: ubuntu-latest permissions: From 084cce51380e812716b0b5db45734f146907e1c4 Mon Sep 17 00:00:00 2001 From: andreouellet Date: Wed, 19 Jun 2024 17:04:43 -0300 Subject: [PATCH 60/61] feat/intial-v-updates-to-tests-and-ghaction-workflows --- .github/workflows/e2e.yaml | 24 ------ .github/workflows/pre-commit.yml | 24 ++++++ .github/workflows/publish-to-pypi.yml | 9 ++- .github/workflows/tests.yml | 73 +++++++++++++++++++ .github/workflows/unit-tests.yml | 27 ------- .gitignore | 2 +- .pre-commit-config.yaml | 73 +++++++++++++++---- .pre-commit-hooks.yaml | 1 + .yamllint.yaml | 35 +++++++++ e2e/precommit-e2e.test | 1 - find_and_replace_strings/main.py | 2 + .../check_version.py | 2 + pypi_bumpversion_check/requirements.txt | 5 ++ pyproject.toml | 13 +--- .../.find-and-replace.json | 14 ++-- tests-package-e2e/README_TEST_PACKAGE.md | 9 +++ .../README_TEST_PACKAGE.md.expected | 9 +++ .../README_TEST_PACKAGE.md.template | 9 +++ .../check-commit-hook.sh | 11 ++- tests-package-e2e/test-package-e2e.sh | 32 ++++++++ tests-pre-commit-hook/.find-and-replace.json | 30 ++++++++ tests-pre-commit-hook/.pre-commit-config.yaml | 14 ++++ .../README_TEST_PRE_COMMIT.md | 9 +++ .../README_TEST_PRE_COMMIT.md.expected | 9 +++ .../README_TEST_PRE_COMMIT.md.template | 9 +++ tests-pre-commit-hook/test-pre-commit-hook.sh | 32 ++++++++ tests/test_main.py | 3 + 27 files changed, 389 insertions(+), 92 deletions(-) delete mode 100644 .github/workflows/e2e.yaml create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .github/workflows/tests.yml delete mode 100644 .github/workflows/unit-tests.yml create mode 100644 .yamllint.yaml delete mode 100644 e2e/precommit-e2e.test rename {assets => pypi_bumpversion_check}/check_version.py (99%) create mode 100644 pypi_bumpversion_check/requirements.txt rename {e2e => tests-package-e2e}/.find-and-replace.json (53%) create mode 100644 tests-package-e2e/README_TEST_PACKAGE.md create mode 100644 tests-package-e2e/README_TEST_PACKAGE.md.expected create mode 100644 tests-package-e2e/README_TEST_PACKAGE.md.template rename {e2e => tests-package-e2e}/check-commit-hook.sh (58%) create mode 100755 tests-package-e2e/test-package-e2e.sh create mode 100644 tests-pre-commit-hook/.find-and-replace.json create mode 100644 tests-pre-commit-hook/.pre-commit-config.yaml create mode 100644 tests-pre-commit-hook/README_TEST_PRE_COMMIT.md create mode 100644 tests-pre-commit-hook/README_TEST_PRE_COMMIT.md.expected create mode 100644 tests-pre-commit-hook/README_TEST_PRE_COMMIT.md.template create mode 100755 tests-pre-commit-hook/test-pre-commit-hook.sh diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml deleted file mode 100644 index b39655e..0000000 --- a/.github/workflows/e2e.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: E2E - -on: - push: - branches: - - main - pull_request: - -jobs: - check-commit-hook: - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.11 - - - name: Run check-commit-hook.sh - run: | - chmod +x e2e/check-commit-hook.sh - ./e2e/check-commit-hook.sh diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..aa35841 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,24 @@ +--- +name: pre-commit + +on: + pull_request: + push: + branches: [main] + +permissions: read-all + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v3 + - name: setup-python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + - name: install-dependencies + run: pip install -r pypi_bumpversion_check/requirements.txt + - name: pre-commit-run + uses: pre-commit/action@v3.0.0 diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index bca27cc..bf35e65 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -1,10 +1,11 @@ +--- name: Publish Python 🐍 distributions 📦 to PyPI on: - pull_request: - branches: - - main - types: [closed] + pull_request: + branches: + - main + types: [closed] jobs: build-n-publish: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..34c33df --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,73 @@ +--- +name: tests + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + # ---------------------- + # JOB 1: Run unit tests + # ---------------------- + tests-unit: + name: tests-unit + runs-on: ubuntu-latest + permissions: + id-token: write + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v5.1.0 + with: + python-version: 3.11 + + - name: Run tests + run: python3 -m unittest tests/test_main.py + # ---------------------- + # JOB 2: Run python package end to end test + # ---------------------- + test-package-e2e: + name: test-package-e2e + runs-on: ubuntu-latest + permissions: + id-token: write + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v5.1.0 + with: + python-version: 3.11 + + - name: Run test-package-e2e.sh + run: | + cd tests-package-e2e + ./test-package-e2e.sh + # ---------------------- + # JOB 3: Run pre-commit hook test + # ---------------------- + test-pre-commit-hook: + name: test-pre-commit-hook + runs-on: ubuntu-latest + permissions: + id-token: write + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v5.1.0 + with: + python-version: 3.11 + + - name: Run test-pre-commit-hook.sh + run: | + cd tests-pre-commit-hook + ./test-pre-commit-hook.sh diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml deleted file mode 100644 index 37bd455..0000000 --- a/.github/workflows/unit-tests.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Unit tests - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - unit-tests: - name: Build and publish Python 🐍 distributions 📦 to PyPI - runs-on: ubuntu-latest - permissions: - id-token: write - - steps: - - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v5.1.0 - with: - python-version: 3.11 - - - name: Run tests - run: python3 -m unittest tests/test_main.py diff --git a/.gitignore b/.gitignore index f8288bf..b2ce492 100644 --- a/.gitignore +++ b/.gitignore @@ -91,7 +91,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a7ef568..270b636 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-merge-conflict - id: check-added-large-files @@ -16,29 +16,72 @@ repos: - id: check-case-conflict - id: mixed-line-ending args: [--fix=lf] - - - repo: https://github.com/zricethezav/gitleaks - rev: v8.18.1 + # ----------------------------- + # Checkov is a static code analysis tool for scanning infrastructure as code (IaC) files for misconfigurations + # that may lead to security or compliance problems. + # ----------------------------- + # Checkov includes more than 750 predefined policies to check for common misconfiguration issues. + # Checkov also supports the creation and contribution of custom policies. + # https://www.checkov.io/4.Integrations/pre-commit.html + # ----------------------------- + - repo: https://github.com/bridgecrewio/checkov.git + rev: 3.2.141 + hooks: + - id: checkov + # ----------------------------- + # Gitleaks SAST tool for detecting and preventing hardcoded secrets like passwords, api keys, and tokens in git repos + # ----------------------------- + # If you are knowingly committing something that is not a secret and gitleaks is catching it, + # you can add an inline comment of '# gitleaks:allow' to the end of that line in your file. + # This will instructs gitleaks to ignore that secret - example: + # some_non_secret_value = a1b2c3d4e5f6g7h8i9j0 # gitleaks:allow + # ----------------------------- + - repo: https://github.com/gitleaks/gitleaks + rev: v8.18.4 hooks: - id: gitleaks - + # ----------------------------- + # Generates Table of Contents in Markdown files + # ----------------------------- + - repo: https://github.com/frnmst/md-toc + rev: 9.0.0 + hooks: + - id: md-toc + args: [-p, github] # CLI options + # ----------------------------- + # YAML Linting on yaml files for pre-commit and github actions + # ----------------------------- + - repo: https://github.com/adrienverge/yamllint + rev: v1.35.1 + hooks: + - id: yamllint + name: Check YAML syntax with yamllint + args: [--strict, -c=.yamllint.yaml, '.'] + always_run: true + pass_filenames: true + # ----------------------------- + # Install PYPI bumpversion check requirements + # ----------------------------- - repo: local hooks: - - id: check-version - name: Check version - entry: python assets/check_version.py + - id: install-pypi_bumpversion_check-requirements + name: Install PYPI bumpversion check requirements + entry: pip install -r pypi_bumpversion_check/requirements.txt language: system files: pyproject.toml - + # ----------------------------- + # PYPI bumpversion check + # ----------------------------- - repo: local hooks: - - id: find-and-replace-strings - name: Find and replace strings - entry: e2e/check-commit-hook.sh + - id: pypi_bumpversion_check + name: Check version + entry: python pypi_bumpversion_check/check_version.py language: system - files: 'e2e/.*\.test$' - verbose: true - + files: pyproject.toml + # ----------------------------- + # Unit Tests + # ----------------------------- - repo: local hooks: - id: unittest diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 362b29c..08472bf 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,3 +1,4 @@ +--- - id: find-and-replace-strings name: find-and-replace-strings description: Finds strings in files and replaces them with other strings. diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..bb6d180 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,35 @@ +--- +yaml-files: + - '*.yaml' + - '*.yml' + - '.yamllint' + +rules: + anchors: enable + braces: enable + brackets: enable + colons: enable + commas: enable + comments: + level: warning + comments-indentation: + level: warning + document-end: disable + document-start: + level: warning + empty-lines: enable + empty-values: disable + float-values: disable + hyphens: enable + indentation: enable + key-duplicates: enable + key-ordering: disable + line-length: + max: 120 + level: warning + new-line-at-end-of-file: enable + new-lines: enable + octal-values: disable + quoted-strings: disable + trailing-spaces: enable + truthy: disable diff --git a/e2e/precommit-e2e.test b/e2e/precommit-e2e.test deleted file mode 100644 index a228fbf..0000000 --- a/e2e/precommit-e2e.test +++ /dev/null @@ -1 +0,0 @@ -# {{PROJECT_NAME}} diff --git a/find_and_replace_strings/main.py b/find_and_replace_strings/main.py index 6b6d38d..45f8487 100755 --- a/find_and_replace_strings/main.py +++ b/find_and_replace_strings/main.py @@ -1,4 +1,6 @@ +#!/usr/bin/env python3 # -*- coding: utf-8 -*- + import os import argparse import fileinput diff --git a/assets/check_version.py b/pypi_bumpversion_check/check_version.py similarity index 99% rename from assets/check_version.py rename to pypi_bumpversion_check/check_version.py index 4318c6a..4392fd3 100644 --- a/assets/check_version.py +++ b/pypi_bumpversion_check/check_version.py @@ -4,6 +4,7 @@ import requests import subprocess + def main(): # Load the pyproject.toml file data = toml.load(open("pyproject.toml")) @@ -25,5 +26,6 @@ def main(): print("The version in pyproject.toml has been changed but not committed. Please commit your changes.") sys.exit(1) + if __name__ == "__main__": main() diff --git a/pypi_bumpversion_check/requirements.txt b/pypi_bumpversion_check/requirements.txt new file mode 100644 index 0000000..a026d3b --- /dev/null +++ b/pypi_bumpversion_check/requirements.txt @@ -0,0 +1,5 @@ +# Requirements for pypi_bumpversion_check +toml +requests +#toml==0.10.2 +#requests==2.26.0 diff --git a/pyproject.toml b/pyproject.toml index da91551..d8fe3bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,5 @@ [build-system] requires = ["setuptools>=61.0", "wheel"] - - - build-backend = "setuptools.build_meta" [tool.setuptools] @@ -10,17 +7,14 @@ packages = ["find_and_replace_strings"] [project] name = "find-and-replace-strings" -version = "1.0.9" +version = "1.1.0" description = "Python package and pre-commit-hook for finding and replacing string(s) in file(s)." readme = "README.md" license = { text = "GPLv3" } authors = [{name = "OpenCEPK Open Cloud Engineering Platform Kit", email = "opencepk@gmail.com"}] -dependencies = [ - 'tomli; python_version < "3.11"', -] requires-python = ">=3.9" - +keywords = ["find", "replace", "string", "file", "pre-commit", "hook", "git", "tool", "utility", "opencepk"] classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", @@ -45,8 +39,5 @@ classifiers = [ "Topic :: Utilities" ] -keywords = ["find", "replace", "string", "file", "pre-commit", "hook", "git", "tool", "utility", "opencepk"] - - [project.scripts] find-and-replace-strings = "find_and_replace_strings.__main__:main" diff --git a/e2e/.find-and-replace.json b/tests-package-e2e/.find-and-replace.json similarity index 53% rename from e2e/.find-and-replace.json rename to tests-package-e2e/.find-and-replace.json index 7d67924..1adb550 100644 --- a/e2e/.find-and-replace.json +++ b/tests-package-e2e/.find-and-replace.json @@ -1,7 +1,7 @@ [ { "search": "{{BUSINESS_UNIT}}", - "replacement": "examp\"lebu" + "replacement": "opencepk" }, { "search": "{{PROJECT_NAME}}", @@ -9,18 +9,22 @@ }, { "search": "{{GITHUB_REPO_URL}}", - "replacement": "https://github.com/examplebu/exampleproject" + "replacement": "https://github.com/opencepk/opencepk-exampleproject" }, { "search": "{{PROJECT_DESCRIPTION_SLUG}}", "replacement": "Example project used to demonstrate all aspects of a project development and deployment" }, { - "search": "tucowsinc/iaascloudenablement", - "replacement": "tucowsinc/example-github-team-name" + "search": "{{PROJECT_TEAM}}", + "replacement": "opencepk/opencepk-exampleteam" }, { "search": "{{PROJECT_CONTRIBUTORS}}", - "replacement": "* [Andre Ouellet](mailto:aouellet@tucowsinc.com)" + "replacement": "* [Open CEPK](mailto:opencepk@gmail.com)" + }, + { + "search": "{{SPECIAL_CHARACTER_TESTS}}", + "replacement": "\"SomeValueInDoubleQuotes\"" } ] diff --git a/tests-package-e2e/README_TEST_PACKAGE.md b/tests-package-e2e/README_TEST_PACKAGE.md new file mode 100644 index 0000000..b0b9c9f --- /dev/null +++ b/tests-package-e2e/README_TEST_PACKAGE.md @@ -0,0 +1,9 @@ +# Target test file for find-and-replace-strings python package + +business unit is: {{BUSINESS_UNIT}} +project name is: {{PROJECT_NAME}} +github url is: {{GITHUB_REPO_URL}} +project description slug is: {{PROJECT_DESCRIPTION_SLUG}} +project team is: {{PROJECT_TEAM}} +project contributors are: {{PROJECT_CONTRIBUTORS}} +special character tests: {{SPECIAL_CHARACTER_TESTS}} diff --git a/tests-package-e2e/README_TEST_PACKAGE.md.expected b/tests-package-e2e/README_TEST_PACKAGE.md.expected new file mode 100644 index 0000000..ac6d9f7 --- /dev/null +++ b/tests-package-e2e/README_TEST_PACKAGE.md.expected @@ -0,0 +1,9 @@ +# Target test file for find-and-replace-strings python package + +business unit is: opencepk +project name is: exampleproject +github url is: https://github.com/opencepk/opencepk-exampleproject +project description slug is: Example project used to demonstrate all aspects of a project development and deployment +project team is: opencepk/opencepk-exampleteam +project contributors are: * [Open CEPK](mailto:opencepk@gmail.com) +special character tests: "SomeValueInDoubleQuotes" diff --git a/tests-package-e2e/README_TEST_PACKAGE.md.template b/tests-package-e2e/README_TEST_PACKAGE.md.template new file mode 100644 index 0000000..b0b9c9f --- /dev/null +++ b/tests-package-e2e/README_TEST_PACKAGE.md.template @@ -0,0 +1,9 @@ +# Target test file for find-and-replace-strings python package + +business unit is: {{BUSINESS_UNIT}} +project name is: {{PROJECT_NAME}} +github url is: {{GITHUB_REPO_URL}} +project description slug is: {{PROJECT_DESCRIPTION_SLUG}} +project team is: {{PROJECT_TEAM}} +project contributors are: {{PROJECT_CONTRIBUTORS}} +special character tests: {{SPECIAL_CHARACTER_TESTS}} diff --git a/e2e/check-commit-hook.sh b/tests-package-e2e/check-commit-hook.sh similarity index 58% rename from e2e/check-commit-hook.sh rename to tests-package-e2e/check-commit-hook.sh index d5d3fbd..053847b 100755 --- a/e2e/check-commit-hook.sh +++ b/tests-package-e2e/check-commit-hook.sh @@ -1,14 +1,17 @@ #!/bin/bash +# Create target test file +cp -f "tests-e2e/target-find-and-replace.txt.template" "tests-e2e/target-find-and-replace.txt" + # Store the original content of the file -original_content=$(cat e2e/precommit-e2e.test) +original_content=$(cat tests-e2e/precommit-e2e.test) echo "Original content: $original_content" # Run the hook -python find_and_replace_strings/main.py --config e2e/.find-and-replace.json e2e/precommit-e2e.test +python find_and_replace_strings/main.py --config tests-e2e/.find-and-replace.json tests-e2e/precommit-e2e.test # Check if the expected changes have been made -content=$(cat e2e/precommit-e2e.test) +content=$(cat tests-e2e/precommit-e2e.test) echo "Content after running the hook: $content" if [[ "$content" != "# exampleproject" ]]; then @@ -18,7 +21,7 @@ if [[ "$content" != "# exampleproject" ]]; then fi # Restore the original content of the file -echo "$original_content" > e2e/precommit-e2e.test +echo "$original_content" > tests-e2e/precommit-e2e.test # If the changes are as expected, print the exit code and exit with a status code of 0 echo "Exit code: 0" diff --git a/tests-package-e2e/test-package-e2e.sh b/tests-package-e2e/test-package-e2e.sh new file mode 100755 index 0000000..8deaebd --- /dev/null +++ b/tests-package-e2e/test-package-e2e.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +TEST_DIR="tests-package-e2e" +TEST_SCRIPT_FILENAME=$(basename -- "$0") +TEST_SCRIPT_NAME="${TEST_SCRIPT_FILENAME%.*}" +TEST_TARGET_FILE="README_TEST_PACKAGE.md" + +#------------------------------- +# Run the python package +#------------------------------- +echo "${TEST_SCRIPT_NAME}: Running pre-commit using ${TEST_DIR}/.pre-commit-config.yaml" +(cd .. && python find_and_replace_strings/main.py --config ${TEST_DIR}/.find-and-replace.json ${TEST_DIR}/${TEST_TARGET_FILE}) + +#------------------------------- +# Evaluate results +#------------------------------- +echo "${TEST_SCRIPT_NAME}: Running (diff ${TEST_TARGET_FILE} ${TEST_TARGET_FILE}.expected)" +echo "${TEST_SCRIPT_NAME}: Fetching return code of diff" +diff "${TEST_TARGET_FILE}" "${TEST_TARGET_FILE}.expected" +evaluate_diff_status=$? + +#------------------------------- +# Run the pre-commit hook reset +#------------------------------- +echo "${TEST_SCRIPT_NAME}: Resetting ${TEST_TARGET_FILE} file to original state" +cp -pf "${TEST_TARGET_FILE}.template" "${TEST_TARGET_FILE}" + +#------------------------------- +# Exit with exit code of diff evaluation +#------------------------------- +echo "${TEST_SCRIPT_NAME}: Exit code = $evaluate_diff_status" +exit $evaluate_diff_status diff --git a/tests-pre-commit-hook/.find-and-replace.json b/tests-pre-commit-hook/.find-and-replace.json new file mode 100644 index 0000000..1adb550 --- /dev/null +++ b/tests-pre-commit-hook/.find-and-replace.json @@ -0,0 +1,30 @@ +[ + { + "search": "{{BUSINESS_UNIT}}", + "replacement": "opencepk" + }, + { + "search": "{{PROJECT_NAME}}", + "replacement": "exampleproject" + }, + { + "search": "{{GITHUB_REPO_URL}}", + "replacement": "https://github.com/opencepk/opencepk-exampleproject" + }, + { + "search": "{{PROJECT_DESCRIPTION_SLUG}}", + "replacement": "Example project used to demonstrate all aspects of a project development and deployment" + }, + { + "search": "{{PROJECT_TEAM}}", + "replacement": "opencepk/opencepk-exampleteam" + }, + { + "search": "{{PROJECT_CONTRIBUTORS}}", + "replacement": "* [Open CEPK](mailto:opencepk@gmail.com)" + }, + { + "search": "{{SPECIAL_CHARACTER_TESTS}}", + "replacement": "\"SomeValueInDoubleQuotes\"" + } +] diff --git a/tests-pre-commit-hook/.pre-commit-config.yaml b/tests-pre-commit-hook/.pre-commit-config.yaml new file mode 100644 index 0000000..f276adb --- /dev/null +++ b/tests-pre-commit-hook/.pre-commit-config.yaml @@ -0,0 +1,14 @@ +--- +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: local + hooks: + - id: find-and-replace-strings + name: find-and-replace-strings + entry: "./find_and_replace_strings/main.py" + language: python + exclude_types: + - binary + args: ["--config", "./tests-pre-commit-hook/.find-and-replace.json"] + files: "tests-pre-commit-hook/README_TEST_PRE_COMMIT.md" diff --git a/tests-pre-commit-hook/README_TEST_PRE_COMMIT.md b/tests-pre-commit-hook/README_TEST_PRE_COMMIT.md new file mode 100644 index 0000000..8757397 --- /dev/null +++ b/tests-pre-commit-hook/README_TEST_PRE_COMMIT.md @@ -0,0 +1,9 @@ +# Target test file for find-and-replace-strings pre-commit hook + +business unit is: {{BUSINESS_UNIT}} +project name is: {{PROJECT_NAME}} +github url is: {{GITHUB_REPO_URL}} +project description slug is: {{PROJECT_DESCRIPTION_SLUG}} +project team is: {{PROJECT_TEAM}} +project contributors are: {{PROJECT_CONTRIBUTORS}} +special character tests: {{SPECIAL_CHARACTER_TESTS}} diff --git a/tests-pre-commit-hook/README_TEST_PRE_COMMIT.md.expected b/tests-pre-commit-hook/README_TEST_PRE_COMMIT.md.expected new file mode 100644 index 0000000..f5752d5 --- /dev/null +++ b/tests-pre-commit-hook/README_TEST_PRE_COMMIT.md.expected @@ -0,0 +1,9 @@ +# Target test file for find-and-replace-strings pre-commit hook + +business unit is: opencepk +project name is: exampleproject +github url is: https://github.com/opencepk/opencepk-exampleproject +project description slug is: Example project used to demonstrate all aspects of a project development and deployment +project team is: opencepk/opencepk-exampleteam +project contributors are: * [Open CEPK](mailto:opencepk@gmail.com) +special character tests: "SomeValueInDoubleQuotes" diff --git a/tests-pre-commit-hook/README_TEST_PRE_COMMIT.md.template b/tests-pre-commit-hook/README_TEST_PRE_COMMIT.md.template new file mode 100644 index 0000000..8757397 --- /dev/null +++ b/tests-pre-commit-hook/README_TEST_PRE_COMMIT.md.template @@ -0,0 +1,9 @@ +# Target test file for find-and-replace-strings pre-commit hook + +business unit is: {{BUSINESS_UNIT}} +project name is: {{PROJECT_NAME}} +github url is: {{GITHUB_REPO_URL}} +project description slug is: {{PROJECT_DESCRIPTION_SLUG}} +project team is: {{PROJECT_TEAM}} +project contributors are: {{PROJECT_CONTRIBUTORS}} +special character tests: {{SPECIAL_CHARACTER_TESTS}} diff --git a/tests-pre-commit-hook/test-pre-commit-hook.sh b/tests-pre-commit-hook/test-pre-commit-hook.sh new file mode 100755 index 0000000..3083a47 --- /dev/null +++ b/tests-pre-commit-hook/test-pre-commit-hook.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +TEST_DIR="tests-pre-commit-hook" +TEST_SCRIPT_FILENAME=$(basename -- "$0") +TEST_SCRIPT_NAME="${TEST_SCRIPT_FILENAME%.*}" +TEST_TARGET_FILE="README_TEST_PRE_COMMIT.md" + +#------------------------------- +# Run the pre-commit hook test +#------------------------------- +echo "${TEST_SCRIPT_NAME}: Running pre-commit using ${TEST_DIR}/.pre-commit-config.yaml" +(cd .. && pre-commit run -a -c "${TEST_DIR}/.pre-commit-config.yaml") + +#------------------------------- +# Evaluate results +#------------------------------- +echo "${TEST_SCRIPT_NAME}: Running (cd ${TEST_DIR} && diff ${TEST_TARGET_FILE} ${TEST_TARGET_FILE}.expected)" +echo "${TEST_SCRIPT_NAME}: Fetching return code of diff" +diff "./${TEST_TARGET_FILE}" "${TEST_TARGET_FILE}.expected" +evaluate_diff_status=$? + +#------------------------------- +# Run the pre-commit hook reset +#------------------------------- +echo "${TEST_SCRIPT_NAME}: Resetting ${TEST_DIR}/${TEST_TARGET_FILE} file to original state" +cp -pf "${TEST_TARGET_FILE}.template" "${TEST_TARGET_FILE}" + +#------------------------------- +# Exit with exit code of diff evaluation +#------------------------------- +echo "${TEST_SCRIPT_NAME}: Exit code = $evaluate_diff_status" +exit $evaluate_diff_status diff --git a/tests/test_main.py b/tests/test_main.py index 78ab455..2241392 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -4,6 +4,7 @@ from unittest.mock import patch, mock_open from find_and_replace_strings.main import replace_in_file, main + class TestMainFunctions(unittest.TestCase): @patch('fileinput.FileInput') def test_replace_in_file(self, mock_fileinput): @@ -17,6 +18,7 @@ def test_replace_in_file(self, mock_fileinput): # Assert that the file was opened correctly mock_fileinput.assert_called_once_with('dummy.txt', inplace=True) + @patch('argparse.ArgumentParser.parse_args') @patch('find_and_replace.main.replace_in_file') @patch('os.getcwd', return_value='/dummy/path') @@ -34,5 +36,6 @@ def test_main(self, mock_json_load, mock_open, mock_getcwd, mock_replace_in_file mock_open.assert_called_once_with('/dummy/path/.find-and-replace.json', 'r') mock_replace_in_file.assert_called_once_with('dummy.txt', 'hello', 'hi') + if __name__ == '__main__': unittest.main() From d750e8896394d637f14d2ef7a8fd3708360e1252 Mon Sep 17 00:00:00 2001 From: andreouellet Date: Wed, 19 Jun 2024 17:08:17 -0300 Subject: [PATCH 61/61] feat/intial-v-fix-ghaction-workflow-tests --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 34c33df..dac0169 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -69,5 +69,6 @@ jobs: - name: Run test-pre-commit-hook.sh run: | + pip install pre-commit cd tests-pre-commit-hook ./test-pre-commit-hook.sh