From d05b5b194eed63604d8a6d1841671d584ab798ef Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@tum.de>
Date: Fri, 15 Apr 2022 13:34:17 +0200
Subject: [PATCH 01/18] first draft alias collision

---
 tools/alias_collision/.gitignore              | 160 ++++++++++++++++++
 .../alias_collision/check_alias_collision.py  |  83 +++++++++
 tools/alias_collision/requirements.txt        |   1 +
 .../test/test_check_alias_collision.py        |  93 ++++++++++
 4 files changed, 337 insertions(+)
 create mode 100644 tools/alias_collision/.gitignore
 create mode 100644 tools/alias_collision/check_alias_collision.py
 create mode 100644 tools/alias_collision/requirements.txt
 create mode 100644 tools/alias_collision/test/test_check_alias_collision.py

diff --git a/tools/alias_collision/.gitignore b/tools/alias_collision/.gitignore
new file mode 100644
index 000000000..68bc17f9f
--- /dev/null
+++ b/tools/alias_collision/.gitignore
@@ -0,0 +1,160 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+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
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+#   This is especially recommended for binary packages to ensure reproducibility, and is more
+#   commonly ignored for libraries.
+#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+#   in version control.
+#   https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+#  and can be added to the global gitignore or merged into this file.  For a more nuclear
+#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
diff --git a/tools/alias_collision/check_alias_collision.py b/tools/alias_collision/check_alias_collision.py
new file mode 100644
index 000000000..0aeba8eb7
--- /dev/null
+++ b/tools/alias_collision/check_alias_collision.py
@@ -0,0 +1,83 @@
+"""Check for alias collisions within the codebase"""
+
+from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, FileType
+from pathlib import Path
+from typing import List
+from dataclasses import dataclass
+import itertools
+import re
+
+
+ERROR_MESSAGE_TEMPLATE = """Found alias collision
+Alias %s defined in %s already exists as %s in %s.
+Consider renaming your alias.
+"""
+
+
+def dir_path(path_string: str) -> Path:
+    if Path(path_string).is_dir():
+        return Path(path_string)
+    else:
+        raise NotADirectoryError(path_string)
+
+
+def parse_arguments():
+    parser = ArgumentParser(
+        description=__doc__,
+        formatter_class=ArgumentDefaultsHelpFormatter,
+    )
+    parser.add_argument(
+        "folder",
+        type=dir_path,
+        help="Folder to check",
+    )
+    return parser.parse_args()
+
+
+@dataclass(frozen=True)
+class Alias:
+
+    alias: str
+    value: str
+    module: Path
+
+
+def find_aliases_in_file(file: Path) -> List[Alias]:
+    matches = re.findall(r"^alias (.*)='(.*)'", file.read_text(), re.M)
+    return [Alias(match[0], match[1], file) for match in matches]
+
+
+def find_all_aliases(path: Path) -> List:
+    files = list(path.rglob("*.zsh"))
+    aliases = [find_aliases_in_file(file) for file in files]
+    return list(itertools.chain(*aliases))
+
+
+def check_for_duplicates(aliases: List[Alias]) -> None:
+    elements = dict()
+    for alias in aliases:
+        if alias.alias in elements:
+            existing = elements[alias.alias]
+            raise ValueError(
+                ERROR_MESSAGE_TEMPLATE
+                % (
+                    f"{alias.alias}={alias.value}",
+                    alias.module.name,
+                    f"{existing.alias}={existing.value}",
+                    existing.module.name,
+                )
+            )
+        else:
+            elements[alias.alias] = alias
+
+
+def main():
+    """main"""
+    args = parse_arguments()
+    aliases = find_all_aliases(args.folder)
+    check_for_duplicates(aliases)
+    print("Found no collisions")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/alias_collision/requirements.txt b/tools/alias_collision/requirements.txt
new file mode 100644
index 000000000..230c9dde1
--- /dev/null
+++ b/tools/alias_collision/requirements.txt
@@ -0,0 +1 @@
+pyfakefs
diff --git a/tools/alias_collision/test/test_check_alias_collision.py b/tools/alias_collision/test/test_check_alias_collision.py
new file mode 100644
index 000000000..0183ed901
--- /dev/null
+++ b/tools/alias_collision/test/test_check_alias_collision.py
@@ -0,0 +1,93 @@
+from pathlib import Path
+
+from pyfakefs.fake_filesystem_unittest import TestCase
+from check_alias_collision import (
+    dir_path,
+    find_all_aliases,
+    find_aliases_in_file,
+    check_for_duplicates,
+    Alias,
+)
+
+
+THREE_ALIASES = """
+alias g='git'
+
+alias ga='git add'
+alias gaa='git add --all'
+"""
+
+CONDITIONAL_ALIAS = """
+is-at-least 2.8 "$git_version" \
+  && alias gfa='git fetch --all --prune --jobs=10' \
+  || alias gfa='git fetch --all --prune'
+"""
+
+
+class CheckAliasCollisionTest(TestCase):
+    def setUp(self) -> None:
+        self.setUpPyfakefs()
+
+    def test_dir_path__is_dir__input_path(self) -> None:
+        self.fs.create_dir("test")
+        self.assertEqual(Path("test"), dir_path("test"))
+
+    def test_dir_path__is_file__raise_not_a_directory_error(self) -> None:
+        self.fs.create_file("test")
+        with self.assertRaises(NotADirectoryError):
+            dir_path("test")
+
+    def test_dir_path__does_not_exist__raise_not_a_directory_error(self) -> None:
+        with self.assertRaises(NotADirectoryError):
+            dir_path("test")
+
+    def test_find_all_aliases__empty_folder_should_return_empty_list(self) -> None:
+        self.fs.create_dir("test")
+        result = find_all_aliases(Path("test"))
+        self.assertListEqual([], result)
+
+    def test_find_aliases_in_file__empty_text_should_return_empty_list(self) -> None:
+        self.fs.create_file("empty.zsh")
+        result = find_aliases_in_file(Path("empty.zsh"))
+        self.assertListEqual([], result)
+
+    def test_find_aliases_in_file__one_alias_should_find_one(self) -> None:
+        self.fs.create_file("one.zsh", contents="alias g='git'")
+        result = find_aliases_in_file(Path("one.zsh"))
+        self.assertListEqual([Alias("g", "git", Path("one.zsh"))], result)
+
+    def test_find_aliases_in_file__three_aliases_should_find_three(self) -> None:
+        self.fs.create_file("three.zsh", contents=THREE_ALIASES)
+        result = find_aliases_in_file(Path("three.zsh"))
+        self.assertListEqual(
+            [
+                Alias("g", "git", Path("three.zsh")),
+                Alias("ga", "git add", Path("three.zsh")),
+                Alias("gaa", "git add --all", Path("three.zsh")),
+            ],
+            result,
+        )
+
+    def test_find_aliases_in_file__one_conditional_alias_should_find_none(self) -> None:
+        self.fs.create_file("conditional.zsh", contents=CONDITIONAL_ALIAS)
+        result = find_aliases_in_file(Path("conditional.zsh"))
+        self.assertListEqual([], result)
+
+    def test_check_for_duplicates__no_duplicates_should_not_raise(self) -> None:
+        check_for_duplicates(
+            [
+                Alias("g", "git", Path("git.zsh")),
+                Alias("ga", "git add", Path("git.zsh")),
+                Alias("gaa", "git add --all", Path("git.zsh")),
+            ]
+        )
+        self.assertTrue(True)
+
+    def test_check_for_duplicates__duplicates_should_raise(self) -> None:
+        with self.assertRaises(ValueError):
+            check_for_duplicates(
+                [
+                    Alias("gc", "git commit", Path("git.zsh")),
+                    Alias("gc", "git clone", Path("git.zsh")),
+                ]
+            )

From de10614369ad5ccdfe3e4fbf81f14b81a4094730 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@tum.de>
Date: Fri, 15 Apr 2022 13:45:25 +0200
Subject: [PATCH 02/18] add collision check to github actions

---
 .github/workflows/main.yml | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 264ac31f3..a51775ced 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -36,3 +36,16 @@ jobs:
                       ./themes/*.zsh-theme; do
             zsh -n "$file" || return 1
           done
+
+  collisions:
+    name: Check alias collisions
+    runs-on: ubuntu-latest
+    steps:
+      - name: Set up git repository
+        uses: actions/checkout@v2
+      - name: Set up Python
+        uses: actions/setup-python@v2
+        with:
+          python-version: 3.8
+      - name: Check for alias collisions
+        run: python3 tools/alias_collision/check_alias_collision.py plugins

From 0d13d8b5fb097eddb2a595dcf933f60b39acacdc Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@tum.de>
Date: Fri, 15 Apr 2022 13:59:24 +0200
Subject: [PATCH 03/18] run python unit tests in CI

---
 .github/workflows/main.yml | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a51775ced..9c5907e74 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -48,4 +48,12 @@ jobs:
         with:
           python-version: 3.8
       - name: Check for alias collisions
-        run: python3 tools/alias_collision/check_alias_collision.py plugins
+        run: python tools/alias_collision/check_alias_collision.py plugins
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r requirements-dev.txt
+      - name: Run unit tests
+        run: |
+          cd tools/alias_collision/
+          python -m unittest discover test

From d0c2053a1df6a4cf54dbbd4d05f91aca18e9131c Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@tum.de>
Date: Tue, 22 Nov 2022 16:24:00 +0100
Subject: [PATCH 04/18] update actions to latest major version

---
 .github/workflows/main.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9c5907e74..ebdde865b 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -42,11 +42,11 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Set up git repository
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Set up Python
-        uses: actions/setup-python@v2
+        uses: actions/setup-python@v4
         with:
-          python-version: 3.8
+          python-version: '3.10'
       - name: Check for alias collisions
         run: python tools/alias_collision/check_alias_collision.py plugins
       - name: Install dependencies

From 821ee1ebec80bf7fff526030f56fbf25fe44c174 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@tum.de>
Date: Tue, 22 Nov 2022 16:24:15 +0100
Subject: [PATCH 05/18] rename test to tests

---
 tools/alias_collision/check_alias_collision.py                | 4 ++--
 .../{test => tests}/test_check_alias_collision.py             | 0
 2 files changed, 2 insertions(+), 2 deletions(-)
 rename tools/alias_collision/{test => tests}/test_check_alias_collision.py (100%)

diff --git a/tools/alias_collision/check_alias_collision.py b/tools/alias_collision/check_alias_collision.py
index 0aeba8eb7..6579974d7 100644
--- a/tools/alias_collision/check_alias_collision.py
+++ b/tools/alias_collision/check_alias_collision.py
@@ -1,6 +1,6 @@
 """Check for alias collisions within the codebase"""
 
-from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, FileType
+from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
 from pathlib import Path
 from typing import List
 from dataclasses import dataclass
@@ -54,7 +54,7 @@ def find_all_aliases(path: Path) -> List:
 
 
 def check_for_duplicates(aliases: List[Alias]) -> None:
-    elements = dict()
+    elements = {}
     for alias in aliases:
         if alias.alias in elements:
             existing = elements[alias.alias]
diff --git a/tools/alias_collision/test/test_check_alias_collision.py b/tools/alias_collision/tests/test_check_alias_collision.py
similarity index 100%
rename from tools/alias_collision/test/test_check_alias_collision.py
rename to tools/alias_collision/tests/test_check_alias_collision.py

From ccfdc8dd97fbc9cde68f4f418ec4bd7911f2658b Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@tum.de>
Date: Tue, 22 Nov 2022 16:35:43 +0100
Subject: [PATCH 06/18] improve error message

---
 tools/alias_collision/check_alias_collision.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/alias_collision/check_alias_collision.py b/tools/alias_collision/check_alias_collision.py
index 6579974d7..e6cbd8586 100644
--- a/tools/alias_collision/check_alias_collision.py
+++ b/tools/alias_collision/check_alias_collision.py
@@ -9,7 +9,7 @@ import re
 
 
 ERROR_MESSAGE_TEMPLATE = """Found alias collision
-Alias %s defined in %s already exists as %s in %s.
+Alias `%s` defined in `%s` already exists as alias `%s` in `%s`.
 Consider renaming your alias.
 """
 

From b456dc5342359b3e5e48e55d42f029fe8240dd06 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@tum.de>
Date: Tue, 22 Nov 2022 17:57:15 +0100
Subject: [PATCH 07/18] print all collisions found before failing

---
 .../alias_collision/check_alias_collision.py  | 57 ++++++++++++-------
 .../tests/test_check_alias_collision.py       | 27 ++++++---
 2 files changed, 56 insertions(+), 28 deletions(-)

diff --git a/tools/alias_collision/check_alias_collision.py b/tools/alias_collision/check_alias_collision.py
index e6cbd8586..ca0fc671f 100644
--- a/tools/alias_collision/check_alias_collision.py
+++ b/tools/alias_collision/check_alias_collision.py
@@ -2,16 +2,14 @@
 
 from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
 from pathlib import Path
-from typing import List
 from dataclasses import dataclass
 import itertools
 import re
 
 
-ERROR_MESSAGE_TEMPLATE = """Found alias collision
-Alias `%s` defined in `%s` already exists as alias `%s` in `%s`.
-Consider renaming your alias.
-"""
+ERROR_MESSAGE_TEMPLATE = (
+    "Alias `%s` defined in `%s` already exists as alias `%s` in `%s`."
+)
 
 
 def dir_path(path_string: str) -> Path:
@@ -42,41 +40,62 @@ class Alias:
     module: Path
 
 
-def find_aliases_in_file(file: Path) -> List[Alias]:
+@dataclass(frozen=True)
+class Collision:
+
+    existing_alias: Alias
+    new_alias: Alias
+
+
+def find_aliases_in_file(file: Path) -> list[Alias]:
     matches = re.findall(r"^alias (.*)='(.*)'", file.read_text(), re.M)
     return [Alias(match[0], match[1], file) for match in matches]
 
 
-def find_all_aliases(path: Path) -> List:
+def find_all_aliases(path: Path) -> list:
     files = list(path.rglob("*.zsh"))
     aliases = [find_aliases_in_file(file) for file in files]
     return list(itertools.chain(*aliases))
 
 
-def check_for_duplicates(aliases: List[Alias]) -> None:
+def check_for_duplicates(aliases: list[Alias]) -> list[Collision]:
     elements = {}
+    collisions = []
     for alias in aliases:
         if alias.alias in elements:
             existing = elements[alias.alias]
-            raise ValueError(
-                ERROR_MESSAGE_TEMPLATE
-                % (
-                    f"{alias.alias}={alias.value}",
-                    alias.module.name,
-                    f"{existing.alias}={existing.value}",
-                    existing.module.name,
-                )
-            )
+            collisions.append(Collision(existing, alias))
         else:
             elements[alias.alias] = alias
+    return collisions
+
+
+def print_collisions(collisions: dict[Alias, Alias]) -> None:
+    if collisions:
+        print(f"Found {len(collisions)} alias collisions:\n")
+        for collision in collisions:
+            print(
+                ERROR_MESSAGE_TEMPLATE
+                % (
+                    f"{collision.new_alias.alias}={collision.new_alias.value}",
+                    collision.new_alias.module.name,
+                    f"{collision.existing_alias.alias}={collision.existing_alias.value}",
+                    collision.existing_alias.module.name,
+                )
+            )
+        print("\nConsider renaming your aliases.")
+    else:
+        print("Found no collisions")
 
 
 def main():
     """main"""
     args = parse_arguments()
     aliases = find_all_aliases(args.folder)
-    check_for_duplicates(aliases)
-    print("Found no collisions")
+    collisions = check_for_duplicates(aliases)
+    print_collisions(collisions)
+    if collisions:
+        exit(-1)
 
 
 if __name__ == "__main__":
diff --git a/tools/alias_collision/tests/test_check_alias_collision.py b/tools/alias_collision/tests/test_check_alias_collision.py
index 0183ed901..4eb0ae343 100644
--- a/tools/alias_collision/tests/test_check_alias_collision.py
+++ b/tools/alias_collision/tests/test_check_alias_collision.py
@@ -7,6 +7,7 @@ from check_alias_collision import (
     find_aliases_in_file,
     check_for_duplicates,
     Alias,
+    Collision,
 )
 
 
@@ -73,21 +74,29 @@ class CheckAliasCollisionTest(TestCase):
         result = find_aliases_in_file(Path("conditional.zsh"))
         self.assertListEqual([], result)
 
-    def test_check_for_duplicates__no_duplicates_should_not_raise(self) -> None:
-        check_for_duplicates(
+    def test_check_for_duplicates__no_duplicates_should_return_empty_dict(self) -> None:
+        result = check_for_duplicates(
             [
                 Alias("g", "git", Path("git.zsh")),
                 Alias("ga", "git add", Path("git.zsh")),
                 Alias("gaa", "git add --all", Path("git.zsh")),
             ]
         )
-        self.assertTrue(True)
+        self.assertListEqual(result, [])
 
-    def test_check_for_duplicates__duplicates_should_raise(self) -> None:
-        with self.assertRaises(ValueError):
-            check_for_duplicates(
-                [
+    def test_check_for_duplicates__duplicates_should_have_one_collision(self) -> None:
+        result = check_for_duplicates(
+            [
+                Alias("gc", "git commit", Path("git.zsh")),
+                Alias("gc", "git clone", Path("git.zsh")),
+            ]
+        )
+        self.assertListEqual(
+            result,
+            [
+                Collision(
                     Alias("gc", "git commit", Path("git.zsh")),
                     Alias("gc", "git clone", Path("git.zsh")),
-                ]
-            )
+                )
+            ],
+        )

From 114240d9fa43bc6a6ca0fdd17d73d69bb8546661 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@tum.de>
Date: Tue, 22 Nov 2022 17:58:17 +0100
Subject: [PATCH 08/18] disable failing of collision checker temporarily

---
 tools/alias_collision/check_alias_collision.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/tools/alias_collision/check_alias_collision.py b/tools/alias_collision/check_alias_collision.py
index ca0fc671f..ac8e2fa20 100644
--- a/tools/alias_collision/check_alias_collision.py
+++ b/tools/alias_collision/check_alias_collision.py
@@ -94,8 +94,9 @@ def main():
     aliases = find_all_aliases(args.folder)
     collisions = check_for_duplicates(aliases)
     print_collisions(collisions)
-    if collisions:
-        exit(-1)
+    # TODO enable once all collisions are fixed
+    # if collisions:
+    #     exit(-1)
 
 
 if __name__ == "__main__":

From 4880743ab7b5b21d80c34fa03d85ffe6a9468de2 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@tum.de>
Date: Tue, 22 Nov 2022 18:35:42 +0100
Subject: [PATCH 09/18] fix CI

---
 .github/workflows/main.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ebdde865b..9170daee7 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -52,8 +52,8 @@ jobs:
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
-          pip install -r requirements-dev.txt
+          pip install -r tools/alias_collision/requirements.txt
       - name: Run unit tests
         run: |
           cd tools/alias_collision/
-          python -m unittest discover test
+          python -m unittest discover tests

From b4a077fdd85b38d06aeb706e54344622df6692b8 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@luminartech.com>
Date: Fri, 2 Feb 2024 13:48:57 +0100
Subject: [PATCH 10/18] refactor to pytest

---
 .github/workflows/main.yml                    |   2 +-
 .../alias_collision/check_alias_collision.py  |   7 +-
 tools/alias_collision/requirements.txt        |   2 +
 .../tests/test_check_alias_collision.py       | 151 ++++++++++--------
 4 files changed, 90 insertions(+), 72 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9170daee7..11b70afe2 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -56,4 +56,4 @@ jobs:
       - name: Run unit tests
         run: |
           cd tools/alias_collision/
-          python -m unittest discover tests
+          python -m pytest
diff --git a/tools/alias_collision/check_alias_collision.py b/tools/alias_collision/check_alias_collision.py
index ac8e2fa20..effb126ec 100644
--- a/tools/alias_collision/check_alias_collision.py
+++ b/tools/alias_collision/check_alias_collision.py
@@ -3,6 +3,7 @@
 from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
 from pathlib import Path
 from dataclasses import dataclass
+from typing import List, Dict
 import itertools
 import re
 
@@ -47,7 +48,7 @@ class Collision:
     new_alias: Alias
 
 
-def find_aliases_in_file(file: Path) -> list[Alias]:
+def find_aliases_in_file(file: Path) -> List[Alias]:
     matches = re.findall(r"^alias (.*)='(.*)'", file.read_text(), re.M)
     return [Alias(match[0], match[1], file) for match in matches]
 
@@ -58,7 +59,7 @@ def find_all_aliases(path: Path) -> list:
     return list(itertools.chain(*aliases))
 
 
-def check_for_duplicates(aliases: list[Alias]) -> list[Collision]:
+def check_for_duplicates(aliases: List[Alias]) -> List[Collision]:
     elements = {}
     collisions = []
     for alias in aliases:
@@ -70,7 +71,7 @@ def check_for_duplicates(aliases: list[Alias]) -> list[Collision]:
     return collisions
 
 
-def print_collisions(collisions: dict[Alias, Alias]) -> None:
+def print_collisions(collisions: Dict[Alias, Alias]) -> None:
     if collisions:
         print(f"Found {len(collisions)} alias collisions:\n")
         for collision in collisions:
diff --git a/tools/alias_collision/requirements.txt b/tools/alias_collision/requirements.txt
index 230c9dde1..edf3ab03b 100644
--- a/tools/alias_collision/requirements.txt
+++ b/tools/alias_collision/requirements.txt
@@ -1 +1,3 @@
 pyfakefs
+pytest
+pytest-sugar
diff --git a/tools/alias_collision/tests/test_check_alias_collision.py b/tools/alias_collision/tests/test_check_alias_collision.py
index 4eb0ae343..5d7c28d2d 100644
--- a/tools/alias_collision/tests/test_check_alias_collision.py
+++ b/tools/alias_collision/tests/test_check_alias_collision.py
@@ -1,6 +1,8 @@
 from pathlib import Path
 
-from pyfakefs.fake_filesystem_unittest import TestCase
+from pyfakefs.fake_filesystem import FakeFilesystem
+import pytest
+
 from check_alias_collision import (
     dir_path,
     find_all_aliases,
@@ -25,78 +27,91 @@ is-at-least 2.8 "$git_version" \
 """
 
 
-class CheckAliasCollisionTest(TestCase):
-    def setUp(self) -> None:
-        self.setUpPyfakefs()
+def test_dir_path__is_dir__input_path(fs: FakeFilesystem) -> None:
+    fs.create_dir("test")
+    assert Path("test") == dir_path("test")
 
-    def test_dir_path__is_dir__input_path(self) -> None:
-        self.fs.create_dir("test")
-        self.assertEqual(Path("test"), dir_path("test"))
 
-    def test_dir_path__is_file__raise_not_a_directory_error(self) -> None:
-        self.fs.create_file("test")
-        with self.assertRaises(NotADirectoryError):
-            dir_path("test")
+def test_dir_path__is_file__raise_not_a_directory_error(fs: FakeFilesystem) -> None:
+    fs.create_file("test")
+    with pytest.raises(NotADirectoryError):
+        dir_path("test")
 
-    def test_dir_path__does_not_exist__raise_not_a_directory_error(self) -> None:
-        with self.assertRaises(NotADirectoryError):
-            dir_path("test")
 
-    def test_find_all_aliases__empty_folder_should_return_empty_list(self) -> None:
-        self.fs.create_dir("test")
-        result = find_all_aliases(Path("test"))
-        self.assertListEqual([], result)
+def test_dir_path__does_not_exist__raise_not_a_directory_error(
+    fs: FakeFilesystem,
+) -> None:
+    with pytest.raises(NotADirectoryError):
+        dir_path("test")
 
-    def test_find_aliases_in_file__empty_text_should_return_empty_list(self) -> None:
-        self.fs.create_file("empty.zsh")
-        result = find_aliases_in_file(Path("empty.zsh"))
-        self.assertListEqual([], result)
 
-    def test_find_aliases_in_file__one_alias_should_find_one(self) -> None:
-        self.fs.create_file("one.zsh", contents="alias g='git'")
-        result = find_aliases_in_file(Path("one.zsh"))
-        self.assertListEqual([Alias("g", "git", Path("one.zsh"))], result)
+def test_find_all_aliases__empty_folder_should_return_empty_list(
+    fs: FakeFilesystem,
+) -> None:
+    fs.create_dir("test")
+    result = find_all_aliases(Path("test"))
+    assert [] == result
 
-    def test_find_aliases_in_file__three_aliases_should_find_three(self) -> None:
-        self.fs.create_file("three.zsh", contents=THREE_ALIASES)
-        result = find_aliases_in_file(Path("three.zsh"))
-        self.assertListEqual(
-            [
-                Alias("g", "git", Path("three.zsh")),
-                Alias("ga", "git add", Path("three.zsh")),
-                Alias("gaa", "git add --all", Path("three.zsh")),
-            ],
-            result,
-        )
-
-    def test_find_aliases_in_file__one_conditional_alias_should_find_none(self) -> None:
-        self.fs.create_file("conditional.zsh", contents=CONDITIONAL_ALIAS)
-        result = find_aliases_in_file(Path("conditional.zsh"))
-        self.assertListEqual([], result)
-
-    def test_check_for_duplicates__no_duplicates_should_return_empty_dict(self) -> None:
-        result = check_for_duplicates(
-            [
-                Alias("g", "git", Path("git.zsh")),
-                Alias("ga", "git add", Path("git.zsh")),
-                Alias("gaa", "git add --all", Path("git.zsh")),
-            ]
-        )
-        self.assertListEqual(result, [])
-
-    def test_check_for_duplicates__duplicates_should_have_one_collision(self) -> None:
-        result = check_for_duplicates(
-            [
-                Alias("gc", "git commit", Path("git.zsh")),
-                Alias("gc", "git clone", Path("git.zsh")),
-            ]
-        )
-        self.assertListEqual(
-            result,
-            [
-                Collision(
-                    Alias("gc", "git commit", Path("git.zsh")),
-                    Alias("gc", "git clone", Path("git.zsh")),
-                )
-            ],
+
+def test_find_aliases_in_file__empty_text_should_return_empty_list(
+    fs: FakeFilesystem,
+) -> None:
+    fs.create_file("empty.zsh")
+    result = find_aliases_in_file(Path("empty.zsh"))
+    assert [] == result
+
+
+def test_find_aliases_in_file__one_alias_should_find_one(fs: FakeFilesystem) -> None:
+    fs.create_file("one.zsh", contents="alias g='git'")
+    result = find_aliases_in_file(Path("one.zsh"))
+    assert [Alias("g", "git", Path("one.zsh"))] == result
+
+
+def test_find_aliases_in_file__three_aliases_should_find_three(
+    fs: FakeFilesystem,
+) -> None:
+    fs.create_file("three.zsh", contents=THREE_ALIASES)
+    result = find_aliases_in_file(Path("three.zsh"))
+    assert [
+        Alias("g", "git", Path("three.zsh")),
+        Alias("ga", "git add", Path("three.zsh")),
+        Alias("gaa", "git add --all", Path("three.zsh")),
+    ] == result
+
+
+def test_find_aliases_in_file__one_conditional_alias_should_find_none(
+    fs: FakeFilesystem,
+) -> None:
+    fs.create_file("conditional.zsh", contents=CONDITIONAL_ALIAS)
+    result = find_aliases_in_file(Path("conditional.zsh"))
+    assert [] == result
+
+
+def test_check_for_duplicates__no_duplicates_should_return_empty_dict(
+    fs: FakeFilesystem,
+) -> None:
+    result = check_for_duplicates(
+        [
+            Alias("g", "git", Path("git.zsh")),
+            Alias("ga", "git add", Path("git.zsh")),
+            Alias("gaa", "git add --all", Path("git.zsh")),
+        ]
+    )
+    assert result == []
+
+
+def test_check_for_duplicates__duplicates_should_have_one_collision(
+    fs: FakeFilesystem,
+) -> None:
+    result = check_for_duplicates(
+        [
+            Alias("gc", "git commit", Path("git.zsh")),
+            Alias("gc", "git clone", Path("git.zsh")),
+        ]
+    )
+    assert result == [
+        Collision(
+            Alias("gc", "git commit", Path("git.zsh")),
+            Alias("gc", "git clone", Path("git.zsh")),
         )
+    ]

From 68f45fe079b133797460a4575f550da3c60bcca0 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@luminartech.com>
Date: Fri, 2 Feb 2024 14:13:12 +0100
Subject: [PATCH 11/18] add filter for known collisions

---
 .../alias_collision/check_alias_collision.py  | 56 +++++++++++++++++--
 .../tests/test_check_alias_collision.py       | 24 ++++++--
 2 files changed, 68 insertions(+), 12 deletions(-)

diff --git a/tools/alias_collision/check_alias_collision.py b/tools/alias_collision/check_alias_collision.py
index effb126ec..9da18112d 100644
--- a/tools/alias_collision/check_alias_collision.py
+++ b/tools/alias_collision/check_alias_collision.py
@@ -12,6 +12,45 @@ ERROR_MESSAGE_TEMPLATE = (
     "Alias `%s` defined in `%s` already exists as alias `%s` in `%s`."
 )
 
+# TODO: We want that list to be empty
+KNOWN_COLLISIONS = [
+    "bcubc",
+    "pbl",
+    "gcd",
+    "h",
+    "brs",
+    "github",
+    "stackoverflow",
+    "zcl",
+    "afs",
+    "allpkgs",
+    "mydeb",
+    "jh",
+    "n",
+    "a",
+    "p",
+    "sf",
+    "sp",
+    "hs",
+    "db",
+    "rn",
+    "rs",
+    "ru",
+    "sc",
+    "sd",
+    "sd",
+    "sp",
+    "c",
+    "dr",
+    "rake",
+    "rubies",
+    "h",
+    "ma",
+    "map",
+    "mis",
+    "m",
+]
+
 
 def dir_path(path_string: str) -> Path:
     if Path(path_string).is_dir():
@@ -47,6 +86,9 @@ class Collision:
     existing_alias: Alias
     new_alias: Alias
 
+    def is_new_collision(self, known_collisions: List[str]) -> bool:
+        return self.new_alias.alias not in known_collisions
+
 
 def find_aliases_in_file(file: Path) -> List[Alias]:
     matches = re.findall(r"^alias (.*)='(.*)'", file.read_text(), re.M)
@@ -54,8 +96,7 @@ def find_aliases_in_file(file: Path) -> List[Alias]:
 
 
 def find_all_aliases(path: Path) -> list:
-    files = list(path.rglob("*.zsh"))
-    aliases = [find_aliases_in_file(file) for file in files]
+    aliases = [find_aliases_in_file(file) for file in path.rglob("*.zsh")]
     return list(itertools.chain(*aliases))
 
 
@@ -94,10 +135,13 @@ def main():
     args = parse_arguments()
     aliases = find_all_aliases(args.folder)
     collisions = check_for_duplicates(aliases)
-    print_collisions(collisions)
-    # TODO enable once all collisions are fixed
-    # if collisions:
-    #     exit(-1)
+    new_collisions = [
+        collision
+        for collision in collisions
+        if collision.is_new_collision(KNOWN_COLLISIONS)
+    ]
+    print_collisions(new_collisions)
+    return -1 if collisions else 0
 
 
 if __name__ == "__main__":
diff --git a/tools/alias_collision/tests/test_check_alias_collision.py b/tools/alias_collision/tests/test_check_alias_collision.py
index 5d7c28d2d..57ca17ed2 100644
--- a/tools/alias_collision/tests/test_check_alias_collision.py
+++ b/tools/alias_collision/tests/test_check_alias_collision.py
@@ -87,9 +87,7 @@ def test_find_aliases_in_file__one_conditional_alias_should_find_none(
     assert [] == result
 
 
-def test_check_for_duplicates__no_duplicates_should_return_empty_dict(
-    fs: FakeFilesystem,
-) -> None:
+def test_check_for_duplicates__no_duplicates_should_return_empty_dict() -> None:
     result = check_for_duplicates(
         [
             Alias("g", "git", Path("git.zsh")),
@@ -100,9 +98,7 @@ def test_check_for_duplicates__no_duplicates_should_return_empty_dict(
     assert result == []
 
 
-def test_check_for_duplicates__duplicates_should_have_one_collision(
-    fs: FakeFilesystem,
-) -> None:
+def test_check_for_duplicates__duplicates_should_have_one_collision() -> None:
     result = check_for_duplicates(
         [
             Alias("gc", "git commit", Path("git.zsh")),
@@ -115,3 +111,19 @@ def test_check_for_duplicates__duplicates_should_have_one_collision(
             Alias("gc", "git clone", Path("git.zsh")),
         )
     ]
+
+
+def test_is_new_collision__new_alias_not_in_known_collisions__should_return_true() -> (
+    None
+):
+    known_collisions = ["gc", "gd"]
+    new_alias = Alias("ga", "git add", Path("git.zsh"))
+    collision = Collision(Alias("gd", "git diff", Path("git.zsh")), new_alias)
+    assert collision.is_new_collision(known_collisions) is True
+
+
+def test_is_new_collision__new_alias_in_known_collisions__should_return_false() -> None:
+    known_collisions = ["gc", "gd", "ga"]
+    new_alias = Alias("ga", "git add", Path("git.zsh"))
+    collision = Collision(Alias("gd", "git diff", Path("git.zsh")), new_alias)
+    assert collision.is_new_collision(known_collisions) is False

From a157b80a72ee5bd67709536d972578d9aef2b98c Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@luminartech.com>
Date: Fri, 2 Feb 2024 19:54:53 +0100
Subject: [PATCH 12/18] read known collisions from file

---
 .../alias_collision/check_alias_collision.py  |  80 ++--
 tools/alias_collision/known_collisions.json   | 422 ++++++++++++++++++
 .../tests/test_check_alias_collision.py       |  37 ++
 3 files changed, 494 insertions(+), 45 deletions(-)
 create mode 100644 tools/alias_collision/known_collisions.json

diff --git a/tools/alias_collision/check_alias_collision.py b/tools/alias_collision/check_alias_collision.py
index 9da18112d..785824ca3 100644
--- a/tools/alias_collision/check_alias_collision.py
+++ b/tools/alias_collision/check_alias_collision.py
@@ -2,54 +2,19 @@
 
 from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
 from pathlib import Path
-from dataclasses import dataclass
+from dataclasses import dataclass, asdict
 from typing import List, Dict
 import itertools
 import re
+import json
 
 
 ERROR_MESSAGE_TEMPLATE = (
     "Alias `%s` defined in `%s` already exists as alias `%s` in `%s`."
 )
 
-# TODO: We want that list to be empty
-KNOWN_COLLISIONS = [
-    "bcubc",
-    "pbl",
-    "gcd",
-    "h",
-    "brs",
-    "github",
-    "stackoverflow",
-    "zcl",
-    "afs",
-    "allpkgs",
-    "mydeb",
-    "jh",
-    "n",
-    "a",
-    "p",
-    "sf",
-    "sp",
-    "hs",
-    "db",
-    "rn",
-    "rs",
-    "ru",
-    "sc",
-    "sd",
-    "sd",
-    "sp",
-    "c",
-    "dr",
-    "rake",
-    "rubies",
-    "h",
-    "ma",
-    "map",
-    "mis",
-    "m",
-]
+# TODO: We want that list to be empty and get rid of this file
+KNOWN_COLLISIONS = Path(__file__).resolve().parent / "known_collisions.json"
 
 
 def dir_path(path_string: str) -> Path:
@@ -69,6 +34,12 @@ def parse_arguments():
         type=dir_path,
         help="Folder to check",
     )
+    parser.add_argument(
+        "--known-collisions",
+        type=Path,
+        default=KNOWN_COLLISIONS,
+        help="Json-serialized list of known collisions",
+    )
     return parser.parse_args()
 
 
@@ -86,8 +57,15 @@ class Collision:
     existing_alias: Alias
     new_alias: Alias
 
-    def is_new_collision(self, known_collisions: List[str]) -> bool:
-        return self.new_alias.alias not in known_collisions
+    def is_new_collision(self, known_collision_aliases: List[str]) -> bool:
+        return self.new_alias.alias not in known_collision_aliases
+
+    @classmethod
+    def from_dict(cls, collision_dict: Dict) -> "Collision":
+        return cls(
+            Alias(**collision_dict["existing_alias"]),
+            Alias(**collision_dict["new_alias"]),
+        )
 
 
 def find_aliases_in_file(file: Path) -> List[Alias]:
@@ -95,6 +73,11 @@ def find_aliases_in_file(file: Path) -> List[Alias]:
     return [Alias(match[0], match[1], file) for match in matches]
 
 
+def load_known_collisions(collision_file: Path) -> List[Collision]:
+    collision_list = json.loads(collision_file.read_text())
+    return [Collision.from_dict(collision_dict) for collision_dict in collision_list]
+
+
 def find_all_aliases(path: Path) -> list:
     aliases = [find_aliases_in_file(file) for file in path.rglob("*.zsh")]
     return list(itertools.chain(*aliases))
@@ -130,19 +113,26 @@ def print_collisions(collisions: Dict[Alias, Alias]) -> None:
         print("Found no collisions")
 
 
-def main():
+def main() -> int:
     """main"""
     args = parse_arguments()
     aliases = find_all_aliases(args.folder)
     collisions = check_for_duplicates(aliases)
+
+    known_collisions = load_known_collisions(args.known_collisions)
+    known_collision_aliases = [
+        collision.new_alias.alias for collision in known_collisions
+    ]
+
     new_collisions = [
         collision
         for collision in collisions
-        if collision.is_new_collision(KNOWN_COLLISIONS)
+        if collision.is_new_collision(known_collision_aliases)
     ]
+
     print_collisions(new_collisions)
-    return -1 if collisions else 0
+    return -1 if new_collisions else 0
 
 
 if __name__ == "__main__":
-    main()
+    exit(main())
diff --git a/tools/alias_collision/known_collisions.json b/tools/alias_collision/known_collisions.json
new file mode 100644
index 000000000..d8c5f3903
--- /dev/null
+++ b/tools/alias_collision/known_collisions.json
@@ -0,0 +1,422 @@
+[
+    {
+        "existing_alias": {
+            "alias": "bcubc",
+            "value": "brew upgrade --cask && brew cleanup",
+            "module": "plugins/brew/brew.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "bcubc",
+            "value": "brew upgrade --cask && brew cleanup",
+            "module": "plugins/brew/brew.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "pbl",
+            "value": "podman build",
+            "module": "plugins/podman/podman.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "pbl",
+            "value": "perlbrew list",
+            "module": "plugins/perl/perl.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "gcd",
+            "value": "git checkout $(git_develop_branch)",
+            "module": "plugins/git/git.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "gcd",
+            "value": "git checkout $(git config gitflow.branch.develop)",
+            "module": "plugins/git-flow/git-flow.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "h",
+            "value": "history",
+            "module": "plugins/history/history.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "h",
+            "value": "history",
+            "module": "plugins/common-aliases/common-aliases.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "brs",
+            "value": "bin/bridgetown start",
+            "module": "plugins/bridgetown/bridgetown.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "brs",
+            "value": "web_search brave",
+            "module": "plugins/web-search/web-search.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "github",
+            "value": "frontend github",
+            "module": "plugins/frontend-search/frontend-search.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "github",
+            "value": "web_search github",
+            "module": "plugins/web-search/web-search.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "stackoverflow",
+            "value": "frontend stackoverflow",
+            "module": "plugins/frontend-search/frontend-search.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "stackoverflow",
+            "value": "web_search stackoverflow",
+            "module": "plugins/web-search/web-search.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "zcl",
+            "value": "sudo zypper clean",
+            "module": "plugins/suse/suse.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "zcl",
+            "value": "sudo zypper cl",
+            "module": "plugins/suse/suse.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "afs",
+            "value": "apt-file search --regexp",
+            "module": "plugins/ubuntu/ubuntu.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "afs",
+            "value": "apt-file search --regexp",
+            "module": "plugins/debian/debian.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "allpkgs",
+            "value": "dpkg --get-selections | grep -v deinstall",
+            "module": "plugins/ubuntu/ubuntu.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "allpkgs",
+            "value": "aptitude search -F \"%p\" --disable-columns ~i",
+            "module": "plugins/debian/debian.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "mydeb",
+            "value": "time dpkg-buildpackage -rfakeroot -us -uc",
+            "module": "plugins/ubuntu/ubuntu.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "mydeb",
+            "value": "time dpkg-buildpackage -rfakeroot -us -uc",
+            "module": "plugins/debian/debian.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "jh",
+            "value": "juju help",
+            "module": "plugins/juju/juju.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "jh",
+            "value": "jhbuild",
+            "module": "plugins/jhbuild/jhbuild.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "n",
+            "value": "nanoc",
+            "module": "plugins/nanoc/nanoc.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "n",
+            "value": "\"$GREP\" -Rvi",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "a",
+            "value": "ansible ",
+            "module": "plugins/ansible/ansible.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "a",
+            "value": "echo >>",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "p",
+            "value": "ps -f",
+            "module": "plugins/common-aliases/common-aliases.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "p",
+            "value": "\"$PAGER\"",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "sf",
+            "value": "`_symfony_console`",
+            "module": "plugins/symfony2/symfony2.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "sf",
+            "value": "\"$ROOT\" \"$GREP\" -Rli",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "sp",
+            "value": "web_search startpage",
+            "module": "plugins/web-search/web-search.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "sp",
+            "value": "\"$ROOT\" \"$PAGER\"",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "hs",
+            "value": "history | grep",
+            "module": "plugins/history/history.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "hs",
+            "value": "hanami server",
+            "module": "plugins/hanami/hanami.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "db",
+            "value": "deno bundle",
+            "module": "plugins/deno/deno.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "db",
+            "value": "dotnet build",
+            "module": "plugins/dotnet/dotnet.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "rn",
+            "value": "react-native",
+            "module": "plugins/react-native/react-native.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "rn",
+            "value": "rails notes",
+            "module": "plugins/rails/rails.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "rs",
+            "value": "repo sync",
+            "module": "plugins/repo/repo.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "rs",
+            "value": "rails server",
+            "module": "plugins/rails/rails.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "ru",
+            "value": "repo upload",
+            "module": "plugins/repo/repo.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "ru",
+            "value": "rails runner",
+            "module": "plugins/rails/rails.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "sc",
+            "value": "\"$ROOT\" cat",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "sc",
+            "value": "ruby script/console",
+            "module": "plugins/rails/rails.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "sd",
+            "value": "\"$ROOT\" \"$WGET\"",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "sd",
+            "value": "ruby script/destroy",
+            "module": "plugins/rails/rails.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "sd",
+            "value": "\"$ROOT\" \"$WGET\"",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "sd",
+            "value": "ruby script/server --debugger",
+            "module": "plugins/rails/rails.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "sp",
+            "value": "web_search startpage",
+            "module": "plugins/web-search/web-search.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "sp",
+            "value": "ruby script/plugin",
+            "module": "plugins/rails/rails.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "c",
+            "value": "cat",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "c",
+            "value": "composer",
+            "module": "plugins/composer/composer.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "dr",
+            "value": "dotnet run",
+            "module": "plugins/dotnet/dotnet.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "dr",
+            "value": "docker container run",
+            "module": "plugins/docker/docker.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "rake",
+            "value": "_rake_command",
+            "module": "plugins/rails/rails.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "rake",
+            "value": "noglob rake",
+            "module": "plugins/rake/rake.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "rubies",
+            "value": "rvm list rubies",
+            "module": "plugins/rvm/rvm.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "rubies",
+            "value": "chruby",
+            "module": "plugins/chruby/chruby.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "h",
+            "value": "history",
+            "module": "plugins/history/history.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "h",
+            "value": "helm",
+            "module": "plugins/helm/helm.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "ma",
+            "value": "mongocli atlas",
+            "module": "plugins/mongocli/mongocli.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "ma",
+            "value": "meteor add",
+            "module": "plugins/meteor/meteor.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "map",
+            "value": "web_search duckduckgo \\!m",
+            "module": "plugins/web-search/web-search.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "map",
+            "value": "meteor add-platform",
+            "module": "plugins/meteor/meteor.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "mis",
+            "value": "microk8s.istio",
+            "module": "plugins/microk8s/microk8s.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "mis",
+            "value": "meteor install-sdk",
+            "module": "plugins/meteor/meteor.plugin.zsh"
+        }
+    },
+    {
+        "existing_alias": {
+            "alias": "m",
+            "value": "man",
+            "module": "plugins/singlechar/singlechar.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "m",
+            "value": "meteor run",
+            "module": "plugins/meteor/meteor.plugin.zsh"
+        }
+    }
+]
diff --git a/tools/alias_collision/tests/test_check_alias_collision.py b/tools/alias_collision/tests/test_check_alias_collision.py
index 57ca17ed2..c6a3802ee 100644
--- a/tools/alias_collision/tests/test_check_alias_collision.py
+++ b/tools/alias_collision/tests/test_check_alias_collision.py
@@ -10,6 +10,7 @@ from check_alias_collision import (
     check_for_duplicates,
     Alias,
     Collision,
+    load_known_collisions,
 )
 
 
@@ -26,6 +27,23 @@ is-at-least 2.8 "$git_version" \
   || alias gfa='git fetch --all --prune'
 """
 
+ONE_KNOWN_COLLISION = """
+[
+    {
+        "existing_alias": {
+            "alias": "gcd",
+            "value": "git checkout $(git_develop_branch)",
+            "module": "plugins/git/git.plugin.zsh"
+        },
+        "new_alias": {
+            "alias": "gcd",
+            "value": "git checkout $(git config gitflow.branch.develop)",
+            "module": "plugins/git-flow/git-flow.plugin.zsh"
+        }
+    }
+]
+"""
+
 
 def test_dir_path__is_dir__input_path(fs: FakeFilesystem) -> None:
     fs.create_dir("test")
@@ -127,3 +145,22 @@ def test_is_new_collision__new_alias_in_known_collisions__should_return_false()
     new_alias = Alias("ga", "git add", Path("git.zsh"))
     collision = Collision(Alias("gd", "git diff", Path("git.zsh")), new_alias)
     assert collision.is_new_collision(known_collisions) is False
+
+
+def test_load_known_collisions__empty_file__should_return_empty_list(
+    fs: FakeFilesystem,
+) -> None:
+    empty_list = Path("empty.json")
+    fs.create_file(empty_list, contents="[]")
+    result = load_known_collisions(empty_list)
+    assert [] == result
+
+
+def test_load_known_collisions__one_collision__should_return_one_collision(
+    fs: FakeFilesystem,
+) -> None:
+    known_collisions_file = Path("known_collisions.json")
+    fs.create_file(known_collisions_file, contents=ONE_KNOWN_COLLISION)
+    result = load_known_collisions(known_collisions_file)
+    assert 1 == len(result)
+    assert "gcd" == result[0].existing_alias.alias

From 7875e78af7b45785d893d60f305f893c43cf77de Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@luminartech.com>
Date: Sat, 3 Feb 2024 10:41:30 +0100
Subject: [PATCH 13/18] removed resolved collision

---
 tools/alias_collision/known_collisions.json | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/tools/alias_collision/known_collisions.json b/tools/alias_collision/known_collisions.json
index d8c5f3903..d20d924fc 100644
--- a/tools/alias_collision/known_collisions.json
+++ b/tools/alias_collision/known_collisions.json
@@ -1,16 +1,4 @@
 [
-    {
-        "existing_alias": {
-            "alias": "bcubc",
-            "value": "brew upgrade --cask && brew cleanup",
-            "module": "plugins/brew/brew.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "bcubc",
-            "value": "brew upgrade --cask && brew cleanup",
-            "module": "plugins/brew/brew.plugin.zsh"
-        }
-    },
     {
         "existing_alias": {
             "alias": "pbl",

From c9282c56138efd5b8182a8a172c8a7b34a1e9b07 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@luminartech.com>
Date: Mon, 19 Feb 2024 15:19:49 +0100
Subject: [PATCH 14/18] remove known collisions

---
 .github/workflows/main.yml                    |  10 +-
 tools/alias_collision/.gitignore              |   3 +
 .../alias_collision/check_alias_collision.py  |  68 ++-
 tools/alias_collision/known_collisions.json   | 410 ------------------
 4 files changed, 59 insertions(+), 432 deletions(-)
 delete mode 100644 tools/alias_collision/known_collisions.json

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 11b70afe2..c9cacf852 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -47,8 +47,6 @@ jobs:
         uses: actions/setup-python@v4
         with:
           python-version: '3.10'
-      - name: Check for alias collisions
-        run: python tools/alias_collision/check_alias_collision.py plugins
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
@@ -57,3 +55,11 @@ jobs:
         run: |
           cd tools/alias_collision/
           python -m pytest
+      - name: Check for alias collisions on target branch
+        run: |
+          git checkout ${GITHUB_BASE_REF}
+          python tools/alias_collision/check_alias_collision.py plugins --known-collisions-output-path known_alias_collisions.json
+      - name: Compare known collisions to new collisions on source branch
+        run: |
+          git checkout ${GITHUB_HEAD_REF}
+          python tools/alias_collision/check_alias_collision.py plugins --known-collisions known_alias_collisions.json
diff --git a/tools/alias_collision/.gitignore b/tools/alias_collision/.gitignore
index 68bc17f9f..ee792c41f 100644
--- a/tools/alias_collision/.gitignore
+++ b/tools/alias_collision/.gitignore
@@ -158,3 +158,6 @@ cython_debug/
 #  and can be added to the global gitignore or merged into this file.  For a more nuclear
 #  option (not recommended) you can uncomment the following to ignore the entire idea folder.
 #.idea/
+
+# default known collisions file
+known_collisions.json
diff --git a/tools/alias_collision/check_alias_collision.py b/tools/alias_collision/check_alias_collision.py
index 785824ca3..fe465a4b7 100644
--- a/tools/alias_collision/check_alias_collision.py
+++ b/tools/alias_collision/check_alias_collision.py
@@ -2,7 +2,7 @@
 
 from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
 from pathlib import Path
-from dataclasses import dataclass, asdict
+from dataclasses import dataclass
 from typing import List, Dict
 import itertools
 import re
@@ -13,8 +13,7 @@ ERROR_MESSAGE_TEMPLATE = (
     "Alias `%s` defined in `%s` already exists as alias `%s` in `%s`."
 )
 
-# TODO: We want that list to be empty and get rid of this file
-KNOWN_COLLISIONS = Path(__file__).resolve().parent / "known_collisions.json"
+KNOWN_COLLISIONS_PATH = Path(__file__).resolve().parent / "known_collisions.json"
 
 
 def dir_path(path_string: str) -> Path:
@@ -34,26 +33,38 @@ def parse_arguments():
         type=dir_path,
         help="Folder to check",
     )
-    parser.add_argument(
+    group = parser.add_mutually_exclusive_group(required=True)
+    group.add_argument(
         "--known-collisions",
         type=Path,
-        default=KNOWN_COLLISIONS,
-        help="Json-serialized list of known collisions",
+        default=None,
+        help="Json-serialized list of known collision to compare to",
+    )
+    group.add_argument(
+        "--known-collisions-output-path",
+        type=Path,
+        default=KNOWN_COLLISIONS_PATH,
+        help="Output path for a json-serialized list of known collisions",
     )
     return parser.parse_args()
 
 
 @dataclass(frozen=True)
 class Alias:
-
     alias: str
     value: str
     module: Path
 
+    def to_dict(self) -> Dict:
+        return {
+            "alias": self.alias,
+            "value": self.value,
+            "module": str(self.module),
+        }
+
 
 @dataclass(frozen=True)
 class Collision:
-
     existing_alias: Alias
     new_alias: Alias
 
@@ -67,6 +78,12 @@ class Collision:
             Alias(**collision_dict["new_alias"]),
         )
 
+    def to_dict(self) -> Dict:
+        return {
+            "existing_alias": self.existing_alias.to_dict(),
+            "new_alias": self.new_alias.to_dict(),
+        }
+
 
 def find_aliases_in_file(file: Path) -> List[Alias]:
     matches = re.findall(r"^alias (.*)='(.*)'", file.read_text(), re.M)
@@ -113,25 +130,36 @@ def print_collisions(collisions: Dict[Alias, Alias]) -> None:
         print("Found no collisions")
 
 
+def check_for_new_collisions(
+    known_collisions: Path, collisions: List[Collision]
+) -> List[Collision]:
+    known_collisions = load_known_collisions(known_collisions)
+    known_collision_aliases = [
+        collision.new_alias.alias for collision in known_collisions
+    ]
+
+    return [
+        collision
+        for collision in collisions
+        if collision.is_new_collision(known_collision_aliases)
+    ]
+
+
 def main() -> int:
     """main"""
     args = parse_arguments()
     aliases = find_all_aliases(args.folder)
     collisions = check_for_duplicates(aliases)
 
-    known_collisions = load_known_collisions(args.known_collisions)
-    known_collision_aliases = [
-        collision.new_alias.alias for collision in known_collisions
-    ]
+    if args.known_collisions is not None:
+        new_collisions = check_for_new_collisions(args.known_collisions, collisions)
+        print_collisions(new_collisions)
+        return -1 if new_collisions else 0
 
-    new_collisions = [
-        collision
-        for collision in collisions
-        if collision.is_new_collision(known_collision_aliases)
-    ]
-
-    print_collisions(new_collisions)
-    return -1 if new_collisions else 0
+    args.known_collisions_output_path.write_text(
+        json.dumps([collision.to_dict() for collision in collisions])
+    )
+    return 0
 
 
 if __name__ == "__main__":
diff --git a/tools/alias_collision/known_collisions.json b/tools/alias_collision/known_collisions.json
deleted file mode 100644
index d20d924fc..000000000
--- a/tools/alias_collision/known_collisions.json
+++ /dev/null
@@ -1,410 +0,0 @@
-[
-    {
-        "existing_alias": {
-            "alias": "pbl",
-            "value": "podman build",
-            "module": "plugins/podman/podman.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "pbl",
-            "value": "perlbrew list",
-            "module": "plugins/perl/perl.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "gcd",
-            "value": "git checkout $(git_develop_branch)",
-            "module": "plugins/git/git.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "gcd",
-            "value": "git checkout $(git config gitflow.branch.develop)",
-            "module": "plugins/git-flow/git-flow.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "h",
-            "value": "history",
-            "module": "plugins/history/history.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "h",
-            "value": "history",
-            "module": "plugins/common-aliases/common-aliases.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "brs",
-            "value": "bin/bridgetown start",
-            "module": "plugins/bridgetown/bridgetown.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "brs",
-            "value": "web_search brave",
-            "module": "plugins/web-search/web-search.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "github",
-            "value": "frontend github",
-            "module": "plugins/frontend-search/frontend-search.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "github",
-            "value": "web_search github",
-            "module": "plugins/web-search/web-search.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "stackoverflow",
-            "value": "frontend stackoverflow",
-            "module": "plugins/frontend-search/frontend-search.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "stackoverflow",
-            "value": "web_search stackoverflow",
-            "module": "plugins/web-search/web-search.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "zcl",
-            "value": "sudo zypper clean",
-            "module": "plugins/suse/suse.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "zcl",
-            "value": "sudo zypper cl",
-            "module": "plugins/suse/suse.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "afs",
-            "value": "apt-file search --regexp",
-            "module": "plugins/ubuntu/ubuntu.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "afs",
-            "value": "apt-file search --regexp",
-            "module": "plugins/debian/debian.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "allpkgs",
-            "value": "dpkg --get-selections | grep -v deinstall",
-            "module": "plugins/ubuntu/ubuntu.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "allpkgs",
-            "value": "aptitude search -F \"%p\" --disable-columns ~i",
-            "module": "plugins/debian/debian.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "mydeb",
-            "value": "time dpkg-buildpackage -rfakeroot -us -uc",
-            "module": "plugins/ubuntu/ubuntu.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "mydeb",
-            "value": "time dpkg-buildpackage -rfakeroot -us -uc",
-            "module": "plugins/debian/debian.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "jh",
-            "value": "juju help",
-            "module": "plugins/juju/juju.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "jh",
-            "value": "jhbuild",
-            "module": "plugins/jhbuild/jhbuild.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "n",
-            "value": "nanoc",
-            "module": "plugins/nanoc/nanoc.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "n",
-            "value": "\"$GREP\" -Rvi",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "a",
-            "value": "ansible ",
-            "module": "plugins/ansible/ansible.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "a",
-            "value": "echo >>",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "p",
-            "value": "ps -f",
-            "module": "plugins/common-aliases/common-aliases.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "p",
-            "value": "\"$PAGER\"",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "sf",
-            "value": "`_symfony_console`",
-            "module": "plugins/symfony2/symfony2.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "sf",
-            "value": "\"$ROOT\" \"$GREP\" -Rli",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "sp",
-            "value": "web_search startpage",
-            "module": "plugins/web-search/web-search.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "sp",
-            "value": "\"$ROOT\" \"$PAGER\"",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "hs",
-            "value": "history | grep",
-            "module": "plugins/history/history.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "hs",
-            "value": "hanami server",
-            "module": "plugins/hanami/hanami.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "db",
-            "value": "deno bundle",
-            "module": "plugins/deno/deno.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "db",
-            "value": "dotnet build",
-            "module": "plugins/dotnet/dotnet.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "rn",
-            "value": "react-native",
-            "module": "plugins/react-native/react-native.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "rn",
-            "value": "rails notes",
-            "module": "plugins/rails/rails.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "rs",
-            "value": "repo sync",
-            "module": "plugins/repo/repo.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "rs",
-            "value": "rails server",
-            "module": "plugins/rails/rails.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "ru",
-            "value": "repo upload",
-            "module": "plugins/repo/repo.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "ru",
-            "value": "rails runner",
-            "module": "plugins/rails/rails.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "sc",
-            "value": "\"$ROOT\" cat",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "sc",
-            "value": "ruby script/console",
-            "module": "plugins/rails/rails.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "sd",
-            "value": "\"$ROOT\" \"$WGET\"",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "sd",
-            "value": "ruby script/destroy",
-            "module": "plugins/rails/rails.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "sd",
-            "value": "\"$ROOT\" \"$WGET\"",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "sd",
-            "value": "ruby script/server --debugger",
-            "module": "plugins/rails/rails.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "sp",
-            "value": "web_search startpage",
-            "module": "plugins/web-search/web-search.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "sp",
-            "value": "ruby script/plugin",
-            "module": "plugins/rails/rails.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "c",
-            "value": "cat",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "c",
-            "value": "composer",
-            "module": "plugins/composer/composer.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "dr",
-            "value": "dotnet run",
-            "module": "plugins/dotnet/dotnet.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "dr",
-            "value": "docker container run",
-            "module": "plugins/docker/docker.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "rake",
-            "value": "_rake_command",
-            "module": "plugins/rails/rails.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "rake",
-            "value": "noglob rake",
-            "module": "plugins/rake/rake.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "rubies",
-            "value": "rvm list rubies",
-            "module": "plugins/rvm/rvm.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "rubies",
-            "value": "chruby",
-            "module": "plugins/chruby/chruby.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "h",
-            "value": "history",
-            "module": "plugins/history/history.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "h",
-            "value": "helm",
-            "module": "plugins/helm/helm.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "ma",
-            "value": "mongocli atlas",
-            "module": "plugins/mongocli/mongocli.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "ma",
-            "value": "meteor add",
-            "module": "plugins/meteor/meteor.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "map",
-            "value": "web_search duckduckgo \\!m",
-            "module": "plugins/web-search/web-search.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "map",
-            "value": "meteor add-platform",
-            "module": "plugins/meteor/meteor.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "mis",
-            "value": "microk8s.istio",
-            "module": "plugins/microk8s/microk8s.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "mis",
-            "value": "meteor install-sdk",
-            "module": "plugins/meteor/meteor.plugin.zsh"
-        }
-    },
-    {
-        "existing_alias": {
-            "alias": "m",
-            "value": "man",
-            "module": "plugins/singlechar/singlechar.plugin.zsh"
-        },
-        "new_alias": {
-            "alias": "m",
-            "value": "meteor run",
-            "module": "plugins/meteor/meteor.plugin.zsh"
-        }
-    }
-]

From 4595a6ffe49a557ff671b629622e50247a61dee5 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@luminartech.com>
Date: Mon, 19 Feb 2024 15:31:48 +0100
Subject: [PATCH 15/18] better use checkout

---
 .github/workflows/main.yml | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index c9cacf852..4385bba05 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -42,7 +42,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Set up git repository
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
       - name: Set up Python
         uses: actions/setup-python@v4
         with:
@@ -55,11 +55,12 @@ jobs:
         run: |
           cd tools/alias_collision/
           python -m pytest
+      - name: Checkout target branch
+        uses: actions/checkout@v4
+        with:
+          path: ohmyzsh-target-branch
+          ref: ${GITHUB_BASE_REF}
       - name: Check for alias collisions on target branch
-        run: |
-          git checkout ${GITHUB_BASE_REF}
-          python tools/alias_collision/check_alias_collision.py plugins --known-collisions-output-path known_alias_collisions.json
+        run: python ohmyzsh-target-branch/tools/alias_collision/check_alias_collision.py plugins --known-collisions-output-path known_alias_collisions.json
       - name: Compare known collisions to new collisions on source branch
-        run: |
-          git checkout ${GITHUB_HEAD_REF}
-          python tools/alias_collision/check_alias_collision.py plugins --known-collisions known_alias_collisions.json
+        run: python tools/alias_collision/check_alias_collision.py plugins --known-collisions known_alias_collisions.json

From e46423299e2814bd61c2fc86ebd9b25bc7af9355 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@luminartech.com>
Date: Mon, 19 Feb 2024 15:37:05 +0100
Subject: [PATCH 16/18] explicitly use master

---
 .github/workflows/main.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 4385bba05..cee780321 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -59,7 +59,7 @@ jobs:
         uses: actions/checkout@v4
         with:
           path: ohmyzsh-target-branch
-          ref: ${GITHUB_BASE_REF}
+          ref: master
       - name: Check for alias collisions on target branch
         run: python ohmyzsh-target-branch/tools/alias_collision/check_alias_collision.py plugins --known-collisions-output-path known_alias_collisions.json
       - name: Compare known collisions to new collisions on source branch

From cdf32e5c04fb7d27e4993786ddcd20a97434e88d Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@luminartech.com>
Date: Mon, 19 Feb 2024 15:39:55 +0100
Subject: [PATCH 17/18] fix plugin path

---
 .github/workflows/main.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index cee780321..be329d166 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -61,6 +61,6 @@ jobs:
           path: ohmyzsh-target-branch
           ref: master
       - name: Check for alias collisions on target branch
-        run: python ohmyzsh-target-branch/tools/alias_collision/check_alias_collision.py plugins --known-collisions-output-path known_alias_collisions.json
+        run: python tools/alias_collision/check_alias_collision.py ohmyzsh-target-branch/plugins --known-collisions-output-path known_alias_collisions.json
       - name: Compare known collisions to new collisions on source branch
         run: python tools/alias_collision/check_alias_collision.py plugins --known-collisions known_alias_collisions.json

From 21f33c476c80bf9562c75b04fddffa8b868932a5 Mon Sep 17 00:00:00 2001
From: Markus Hofbauer <markus.hofbauer@luminartech.com>
Date: Mon, 19 Feb 2024 18:39:33 +0100
Subject: [PATCH 18/18] move tool to .github workflows

---
 {tools => .github/workflows}/alias_collision/.gitignore   | 0
 .../workflows}/alias_collision/check_alias_collision.py   | 0
 .../workflows}/alias_collision/requirements.txt           | 0
 .../alias_collision/tests/test_check_alias_collision.py   | 0
 .github/workflows/main.yml                                | 8 ++++----
 5 files changed, 4 insertions(+), 4 deletions(-)
 rename {tools => .github/workflows}/alias_collision/.gitignore (100%)
 rename {tools => .github/workflows}/alias_collision/check_alias_collision.py (100%)
 rename {tools => .github/workflows}/alias_collision/requirements.txt (100%)
 rename {tools => .github/workflows}/alias_collision/tests/test_check_alias_collision.py (100%)

diff --git a/tools/alias_collision/.gitignore b/.github/workflows/alias_collision/.gitignore
similarity index 100%
rename from tools/alias_collision/.gitignore
rename to .github/workflows/alias_collision/.gitignore
diff --git a/tools/alias_collision/check_alias_collision.py b/.github/workflows/alias_collision/check_alias_collision.py
similarity index 100%
rename from tools/alias_collision/check_alias_collision.py
rename to .github/workflows/alias_collision/check_alias_collision.py
diff --git a/tools/alias_collision/requirements.txt b/.github/workflows/alias_collision/requirements.txt
similarity index 100%
rename from tools/alias_collision/requirements.txt
rename to .github/workflows/alias_collision/requirements.txt
diff --git a/tools/alias_collision/tests/test_check_alias_collision.py b/.github/workflows/alias_collision/tests/test_check_alias_collision.py
similarity index 100%
rename from tools/alias_collision/tests/test_check_alias_collision.py
rename to .github/workflows/alias_collision/tests/test_check_alias_collision.py
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index be329d166..048a09231 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -50,10 +50,10 @@ jobs:
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
-          pip install -r tools/alias_collision/requirements.txt
+          pip install -r .github/workflows/alias_collision/requirements.txt
       - name: Run unit tests
         run: |
-          cd tools/alias_collision/
+          cd .github/workflows/alias_collision/
           python -m pytest
       - name: Checkout target branch
         uses: actions/checkout@v4
@@ -61,6 +61,6 @@ jobs:
           path: ohmyzsh-target-branch
           ref: master
       - name: Check for alias collisions on target branch
-        run: python tools/alias_collision/check_alias_collision.py ohmyzsh-target-branch/plugins --known-collisions-output-path known_alias_collisions.json
+        run: python .github/workflows/alias_collision/check_alias_collision.py ohmyzsh-target-branch/plugins --known-collisions-output-path known_alias_collisions.json
       - name: Compare known collisions to new collisions on source branch
-        run: python tools/alias_collision/check_alias_collision.py plugins --known-collisions known_alias_collisions.json
+        run: python .github/workflows/alias_collision/check_alias_collision.py plugins --known-collisions known_alias_collisions.json