From 5afc0cb2531e1ec982764564ea7cfb960dc7bfa7 Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Thu, 21 May 2026 15:58:41 +0300 Subject: [PATCH 01/10] feat: add kodjin-cli installer and GitHubRepoManager --- .gitignore | 2 + pyproject.toml | 3 +- src/github_actions/common/__init__.py | 7 +++- .../common/actions/init_project.py | 14 ++++++- .../common/utils/github_repo_manager.py | 21 +++++++++++ .../common/utils/install_kodjin_cli.py | 37 +++++++++++++++++++ 6 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 src/github_actions/common/utils/github_repo_manager.py create mode 100644 src/github_actions/common/utils/install_kodjin_cli.py diff --git a/.gitignore b/.gitignore index 663b20c..934dda8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ dist/ build/ __pycache__/ +pyproject.toml +uv.lock diff --git a/pyproject.toml b/pyproject.toml index 053d647..641db90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,8 @@ dependencies = [ "packaging~=24.2", "utils~=1.0.2", "boto3~=1.37.18", - "botocore~=1.37.18" + "botocore~=1.37.18", + "gitpython~=3.1.0", ] [build-system] diff --git a/src/github_actions/common/__init__.py b/src/github_actions/common/__init__.py index dd33709..d9970bd 100644 --- a/src/github_actions/common/__init__.py +++ b/src/github_actions/common/__init__.py @@ -1,6 +1,6 @@ # github_actions/common/__init__.py -from .actions.init_project import GETTenant, ProjectInitializer, RMKConfigInitCommand +from .actions.init_project import GETTenant, ProjectInitializer, RMKConfigInitCommand, GetKodjinRootDomain from .credentials.cluster_provider_credentials import ( AWSConfig, AzureConfig, ClusterProviders, Credentials, EnvironmentConfig, GCPConfig ) @@ -17,6 +17,8 @@ from .utils.cmd import BaseCommand, CMDInterface from .utils.github_environment_variables import GitHubContext from .utils.install_rmk import RMKInstaller +from .utils.install_kodjin_cli import KodjinCLIInstaller +from .utils.github_repo_manager import GitHubRepoManager __all__ = [ @@ -45,4 +47,7 @@ "RMKInstaller", "S3BucketManager", "SlackNotifier", + "KodjinCLIInstaller", + "GitHubRepoManager", + "GetKodjinRootDomain", ] diff --git a/src/github_actions/common/actions/init_project.py b/src/github_actions/common/actions/init_project.py index 8dcda32..059ca3d 100644 --- a/src/github_actions/common/actions/init_project.py +++ b/src/github_actions/common/actions/init_project.py @@ -47,10 +47,22 @@ def execute(self) -> str: return self.run() def run(self) -> str: - output = self.run_command(f"rmk --log-format=json config view", True) + output = self.run_command("rmk --log-format=json config view", True) rmk_config = json.loads(output) return rmk_config["config"]["Tenant"] +class GetKodjinRootDomain(BaseCommand, CMDInterface): + def __init__(self, environment: str): + super().__init__(environment) + + def execute(self) -> str: + return self.run() + + def run(self) -> str: + output = self.run_command("rmk --log-format=json config view", True) + rmk_config = json.loads(output) + return rmk_config["config"]["RootDomain"] + class ProjectInitializer: GIT_CONFIG = { diff --git a/src/github_actions/common/utils/github_repo_manager.py b/src/github_actions/common/utils/github_repo_manager.py new file mode 100644 index 0000000..611e6bf --- /dev/null +++ b/src/github_actions/common/utils/github_repo_manager.py @@ -0,0 +1,21 @@ +from git import Repo + + +class GitHubRepoManager: + def __init__(self, local_dir: str, repo_url: str, branch: str | None = None, github_token: str | None = None): + self.local_dir = local_dir + self.repo_url = repo_url + self.branch = branch + self.github_token = github_token + + def clone_or_update_repo(self): + print("Cloning repository...") + + env = None + if self.github_token and len(self.github_token) > 0: + env = {"GITHUB_TOKEN": self.github_token} + + repo = Repo.clone_from(self.repo_url, self.local_dir, env=env) + if self.branch: + repo.heads[self.branch].checkout() + print(f"Repository {self.repo_url} cloned successfully!") diff --git a/src/github_actions/common/utils/install_kodjin_cli.py b/src/github_actions/common/utils/install_kodjin_cli.py new file mode 100644 index 0000000..321ad4e --- /dev/null +++ b/src/github_actions/common/utils/install_kodjin_cli.py @@ -0,0 +1,37 @@ +import subprocess +import requests + +from argparse import Namespace +from packaging import version + +class KodjinCLIInstaller: + def __init__(self, args: Namespace): + self.version = args.kodjin_cli_version + self.url = args.kodjin_cli_download_url + self.verify_kodjin_cli_version() + self.install_kodjin_cli() + + def verify_kodjin_cli_version(self): + print("Verifying Kodjin CLI installation version...") + if self.version != "latest": + if version.parse(self.version) <= version.parse('v0.1.11'): + raise Exception(f"version {self.version} of Kodjin CLI is not correct, " + + "the version for Kodjin CLI must be at least v0.1.11 or greater") + + def install_kodjin_cli(self): + print("Installing Kodjin CLI.") + try: + response = requests.get(self.url) + response.raise_for_status() + except requests.RequestException as err: + raise Exception(f"downloading Kodjin CLI installer file:\n{err}") + + try: + subprocess.run( + ["bash", "-s", "--", self.version], + check=True, + text=True, + input=response.text + ) + except subprocess.CalledProcessError as err: + raise Exception(f"installing Kodjin CLI:\n{err}") \ No newline at end of file From 26e68e6429d4ebb289603c172c86c1170b068d0c Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Fri, 22 May 2026 11:36:47 +0300 Subject: [PATCH 02/10] fix: update GitHubRepoManager to include GIT_ASKPASS and GIT_PASSWORD in the environment variables for cloning --- pyproject.toml | 3 +-- src/github_actions/common/utils/github_repo_manager.py | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 641db90..053d647 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,8 +31,7 @@ dependencies = [ "packaging~=24.2", "utils~=1.0.2", "boto3~=1.37.18", - "botocore~=1.37.18", - "gitpython~=3.1.0", + "botocore~=1.37.18" ] [build-system] diff --git a/src/github_actions/common/utils/github_repo_manager.py b/src/github_actions/common/utils/github_repo_manager.py index 611e6bf..afd95d9 100644 --- a/src/github_actions/common/utils/github_repo_manager.py +++ b/src/github_actions/common/utils/github_repo_manager.py @@ -13,9 +13,11 @@ def clone_or_update_repo(self): env = None if self.github_token and len(self.github_token) > 0: - env = {"GITHUB_TOKEN": self.github_token} + env = {"GITHUB_TOKEN": self.github_token, "GIT_ASKPASS": "echo", "GIT_PASSWORD": self.github_token} + print(f"Using GitHub token: {'****' + self.github_token[-4:]}") repo = Repo.clone_from(self.repo_url, self.local_dir, env=env) + if self.branch: repo.heads[self.branch].checkout() print(f"Repository {self.repo_url} cloned successfully!") From 84bf065bfac4589a423431c6ef7721a8631b323e Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Fri, 22 May 2026 12:13:24 +0300 Subject: [PATCH 03/10] refactor: streamline environment variable handling in GitHubRepoManager --- src/github_actions/common/utils/github_repo_manager.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/github_actions/common/utils/github_repo_manager.py b/src/github_actions/common/utils/github_repo_manager.py index afd95d9..ac7845a 100644 --- a/src/github_actions/common/utils/github_repo_manager.py +++ b/src/github_actions/common/utils/github_repo_manager.py @@ -1,3 +1,5 @@ +import os + from git import Repo @@ -11,12 +13,13 @@ def __init__(self, local_dir: str, repo_url: str, branch: str | None = None, git def clone_or_update_repo(self): print("Cloning repository...") - env = None if self.github_token and len(self.github_token) > 0: - env = {"GITHUB_TOKEN": self.github_token, "GIT_ASKPASS": "echo", "GIT_PASSWORD": self.github_token} + os.environ["GITHUB_TOKEN"] = self.github_token + os.environ["GIT_ASKPASS"] = "echo" + os.environ["GIT_PASSWORD"] = os.environ["GITHUB_TOKEN"] print(f"Using GitHub token: {'****' + self.github_token[-4:]}") - repo = Repo.clone_from(self.repo_url, self.local_dir, env=env) + repo = Repo.clone_from(self.repo_url, self.local_dir) if self.branch: repo.heads[self.branch].checkout() From f8ccc25cabaaa4378fe824dd0571a4c73efd0bc7 Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Fri, 22 May 2026 12:42:19 +0300 Subject: [PATCH 04/10] fix: handle exceptions during repository cloning in GitHubRepoManager --- .../common/utils/github_repo_manager.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/github_actions/common/utils/github_repo_manager.py b/src/github_actions/common/utils/github_repo_manager.py index ac7845a..7c087b4 100644 --- a/src/github_actions/common/utils/github_repo_manager.py +++ b/src/github_actions/common/utils/github_repo_manager.py @@ -13,14 +13,18 @@ def __init__(self, local_dir: str, repo_url: str, branch: str | None = None, git def clone_or_update_repo(self): print("Cloning repository...") - if self.github_token and len(self.github_token) > 0: - os.environ["GITHUB_TOKEN"] = self.github_token - os.environ["GIT_ASKPASS"] = "echo" - os.environ["GIT_PASSWORD"] = os.environ["GITHUB_TOKEN"] - print(f"Using GitHub token: {'****' + self.github_token[-4:]}") + try: + if self.github_token and len(self.github_token) > 0: + os.environ["GITHUB_TOKEN"] = self.github_token + os.environ["GIT_ASKPASS"] = "echo" + os.environ["GIT_PASSWORD"] = os.environ["GITHUB_TOKEN"] + print(f"Using GitHub token: {'****' + self.github_token[-4:]}") - repo = Repo.clone_from(self.repo_url, self.local_dir) + repo = Repo.clone_from(self.repo_url, self.local_dir) - if self.branch: - repo.heads[self.branch].checkout() - print(f"Repository {self.repo_url} cloned successfully!") + if self.branch: + repo.heads[self.branch].checkout() + print(f"Repository {self.repo_url} cloned successfully!") + except Exception as e: + print(f"Error cloning repository: {e}") + raise From 2a2676f0d689969e5df7be64bae0e00742dfea3d Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Fri, 22 May 2026 12:46:31 +0300 Subject: [PATCH 05/10] fix: update GitHubRepoManager to use a temporary askpass script for authentication --- .../common/utils/github_repo_manager.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/github_actions/common/utils/github_repo_manager.py b/src/github_actions/common/utils/github_repo_manager.py index 7c087b4..7efbf86 100644 --- a/src/github_actions/common/utils/github_repo_manager.py +++ b/src/github_actions/common/utils/github_repo_manager.py @@ -15,10 +15,23 @@ def clone_or_update_repo(self): try: if self.github_token and len(self.github_token) > 0: - os.environ["GITHUB_TOKEN"] = self.github_token - os.environ["GIT_ASKPASS"] = "echo" - os.environ["GIT_PASSWORD"] = os.environ["GITHUB_TOKEN"] - print(f"Using GitHub token: {'****' + self.github_token[-4:]}") + # Створюємо тимчасовий askpass скрипт + askpass_path = os.path.abspath("askpass.sh") + with open(askpass_path, "w") as f: + # Якщо Git просить Username — повертаємо будь-який текст (напр. 'git') + # Якщо просить Password — повертаємо сам токен + f.write(f'''#!/bin/sh + case "$1" in + *Username*) echo "git" ;; + *Password*) echo "{self.github_token}" ;; + esac + ''') + + # Робимо скрипт виконуваним + os.chmod(askpass_path, 0o700) + + # Задаємо змінну оточення для Git + os.environ["GIT_ASKPASS"] = askpass_path repo = Repo.clone_from(self.repo_url, self.local_dir) From 9fa013c0a6576c7d90031a920b271991e64b14b6 Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Fri, 22 May 2026 14:07:42 +0300 Subject: [PATCH 06/10] refactor: update comments in GitHubRepoManager for clarity and consistency --- .../common/utils/github_repo_manager.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/github_actions/common/utils/github_repo_manager.py b/src/github_actions/common/utils/github_repo_manager.py index 7efbf86..30a2661 100644 --- a/src/github_actions/common/utils/github_repo_manager.py +++ b/src/github_actions/common/utils/github_repo_manager.py @@ -15,28 +15,30 @@ def clone_or_update_repo(self): try: if self.github_token and len(self.github_token) > 0: - # Створюємо тимчасовий askpass скрипт + # create askpass script to provide token to Git when it prompts for credentials askpass_path = os.path.abspath("askpass.sh") with open(askpass_path, "w") as f: - # Якщо Git просить Username — повертаємо будь-який текст (напр. 'git') - # Якщо просить Password — повертаємо сам токен + # If Git prompts for Username — we return "git" (or any non-empty string) + # If Git prompts for Password — we return the actual token f.write(f'''#!/bin/sh - case "$1" in - *Username*) echo "git" ;; - *Password*) echo "{self.github_token}" ;; - esac - ''') + case "$1" in + *Username*) echo "git" ;; + *Password*) echo "{self.github_token}" ;; + esac + ''') - # Робимо скрипт виконуваним + # Make the script executable os.chmod(askpass_path, 0o700) - # Задаємо змінну оточення для Git + # Set the environment variable for Git os.environ["GIT_ASKPASS"] = askpass_path repo = Repo.clone_from(self.repo_url, self.local_dir) if self.branch: repo.heads[self.branch].checkout() + print(f"Checked out branch: {self.branch}") + print(f"Repository {self.repo_url} cloned successfully!") except Exception as e: print(f"Error cloning repository: {e}") From a7f2a17c7463a5dcfb0df4770b78c47fb6f47380 Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Fri, 22 May 2026 14:56:32 +0300 Subject: [PATCH 07/10] refactor: improve code formatting and readability in GitHubRepoManager and KodjinCLIInstaller --- .../common/utils/github_repo_manager.py | 11 ++++++++++- .../common/utils/install_kodjin_cli.py | 13 ++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/github_actions/common/utils/github_repo_manager.py b/src/github_actions/common/utils/github_repo_manager.py index 30a2661..1947ba9 100644 --- a/src/github_actions/common/utils/github_repo_manager.py +++ b/src/github_actions/common/utils/github_repo_manager.py @@ -4,7 +4,13 @@ class GitHubRepoManager: - def __init__(self, local_dir: str, repo_url: str, branch: str | None = None, github_token: str | None = None): + def __init__( + self, + local_dir: str, + repo_url: str, + branch: str | None = None, + github_token: str | None = None, + ): self.local_dir = local_dir self.repo_url = repo_url self.branch = branch @@ -39,6 +45,9 @@ def clone_or_update_repo(self): repo.heads[self.branch].checkout() print(f"Checked out branch: {self.branch}") + # print ls + print(f"Contents of {self.local_dir}: {os.listdir(self.local_dir)}") + print(f"Repository {self.repo_url} cloned successfully!") except Exception as e: print(f"Error cloning repository: {e}") diff --git a/src/github_actions/common/utils/install_kodjin_cli.py b/src/github_actions/common/utils/install_kodjin_cli.py index 321ad4e..dba5ac0 100644 --- a/src/github_actions/common/utils/install_kodjin_cli.py +++ b/src/github_actions/common/utils/install_kodjin_cli.py @@ -4,6 +4,7 @@ from argparse import Namespace from packaging import version + class KodjinCLIInstaller: def __init__(self, args: Namespace): self.version = args.kodjin_cli_version @@ -14,9 +15,11 @@ def __init__(self, args: Namespace): def verify_kodjin_cli_version(self): print("Verifying Kodjin CLI installation version...") if self.version != "latest": - if version.parse(self.version) <= version.parse('v0.1.11'): - raise Exception(f"version {self.version} of Kodjin CLI is not correct, " + - "the version for Kodjin CLI must be at least v0.1.11 or greater") + if version.parse(self.version) <= version.parse("v0.1.11"): + raise Exception( + f"version {self.version} of Kodjin CLI is not correct, " + + "the version for Kodjin CLI must be at least v0.1.11 or greater" + ) def install_kodjin_cli(self): print("Installing Kodjin CLI.") @@ -31,7 +34,7 @@ def install_kodjin_cli(self): ["bash", "-s", "--", self.version], check=True, text=True, - input=response.text + input=response.text, ) except subprocess.CalledProcessError as err: - raise Exception(f"installing Kodjin CLI:\n{err}") \ No newline at end of file + raise Exception(f"installing Kodjin CLI:\n{err}") From cc57b11b5d67f8467fc7a9c3b559c60cd7537115 Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Fri, 22 May 2026 14:59:57 +0300 Subject: [PATCH 08/10] refactor: remove unnecessary print statement for directory contents in GitHubRepoManager --- src/github_actions/common/utils/github_repo_manager.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/github_actions/common/utils/github_repo_manager.py b/src/github_actions/common/utils/github_repo_manager.py index 1947ba9..34d3de8 100644 --- a/src/github_actions/common/utils/github_repo_manager.py +++ b/src/github_actions/common/utils/github_repo_manager.py @@ -45,9 +45,6 @@ def clone_or_update_repo(self): repo.heads[self.branch].checkout() print(f"Checked out branch: {self.branch}") - # print ls - print(f"Contents of {self.local_dir}: {os.listdir(self.local_dir)}") - print(f"Repository {self.repo_url} cloned successfully!") except Exception as e: print(f"Error cloning repository: {e}") From 9f6ee123210448e49d9be68bd7ac97001ad57037 Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Wed, 27 May 2026 19:18:26 +0300 Subject: [PATCH 09/10] refactor: rename GetKodjinRootDomain to GetRootDomain and remove GitHubRepoManager --- src/github_actions/common/__init__.py | 6 +-- .../common/actions/init_project.py | 2 +- .../common/utils/github_repo_manager.py | 51 ------------------- 3 files changed, 3 insertions(+), 56 deletions(-) delete mode 100644 src/github_actions/common/utils/github_repo_manager.py diff --git a/src/github_actions/common/__init__.py b/src/github_actions/common/__init__.py index d9970bd..87da7e4 100644 --- a/src/github_actions/common/__init__.py +++ b/src/github_actions/common/__init__.py @@ -1,6 +1,6 @@ # github_actions/common/__init__.py -from .actions.init_project import GETTenant, ProjectInitializer, RMKConfigInitCommand, GetKodjinRootDomain +from .actions.init_project import GETTenant, ProjectInitializer, RMKConfigInitCommand, GetRootDomain from .credentials.cluster_provider_credentials import ( AWSConfig, AzureConfig, ClusterProviders, Credentials, EnvironmentConfig, GCPConfig ) @@ -18,7 +18,6 @@ from .utils.github_environment_variables import GitHubContext from .utils.install_rmk import RMKInstaller from .utils.install_kodjin_cli import KodjinCLIInstaller -from .utils.github_repo_manager import GitHubRepoManager __all__ = [ @@ -48,6 +47,5 @@ "S3BucketManager", "SlackNotifier", "KodjinCLIInstaller", - "GitHubRepoManager", - "GetKodjinRootDomain", + "GetRootDomain", ] diff --git a/src/github_actions/common/actions/init_project.py b/src/github_actions/common/actions/init_project.py index 059ca3d..95ab590 100644 --- a/src/github_actions/common/actions/init_project.py +++ b/src/github_actions/common/actions/init_project.py @@ -51,7 +51,7 @@ def run(self) -> str: rmk_config = json.loads(output) return rmk_config["config"]["Tenant"] -class GetKodjinRootDomain(BaseCommand, CMDInterface): +class GetRootDomain(BaseCommand, CMDInterface): def __init__(self, environment: str): super().__init__(environment) diff --git a/src/github_actions/common/utils/github_repo_manager.py b/src/github_actions/common/utils/github_repo_manager.py deleted file mode 100644 index 34d3de8..0000000 --- a/src/github_actions/common/utils/github_repo_manager.py +++ /dev/null @@ -1,51 +0,0 @@ -import os - -from git import Repo - - -class GitHubRepoManager: - def __init__( - self, - local_dir: str, - repo_url: str, - branch: str | None = None, - github_token: str | None = None, - ): - self.local_dir = local_dir - self.repo_url = repo_url - self.branch = branch - self.github_token = github_token - - def clone_or_update_repo(self): - print("Cloning repository...") - - try: - if self.github_token and len(self.github_token) > 0: - # create askpass script to provide token to Git when it prompts for credentials - askpass_path = os.path.abspath("askpass.sh") - with open(askpass_path, "w") as f: - # If Git prompts for Username — we return "git" (or any non-empty string) - # If Git prompts for Password — we return the actual token - f.write(f'''#!/bin/sh - case "$1" in - *Username*) echo "git" ;; - *Password*) echo "{self.github_token}" ;; - esac - ''') - - # Make the script executable - os.chmod(askpass_path, 0o700) - - # Set the environment variable for Git - os.environ["GIT_ASKPASS"] = askpass_path - - repo = Repo.clone_from(self.repo_url, self.local_dir) - - if self.branch: - repo.heads[self.branch].checkout() - print(f"Checked out branch: {self.branch}") - - print(f"Repository {self.repo_url} cloned successfully!") - except Exception as e: - print(f"Error cloning repository: {e}") - raise From b353bf87a97107907cbc79031462c5f67004c020 Mon Sep 17 00:00:00 2001 From: Bohdan Hrytsenko Date: Thu, 28 May 2026 10:52:05 +0300 Subject: [PATCH 10/10] refactor: add a blank line before GetRootDomain class --- src/github_actions/common/actions/init_project.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/github_actions/common/actions/init_project.py b/src/github_actions/common/actions/init_project.py index 95ab590..7e2f6ec 100644 --- a/src/github_actions/common/actions/init_project.py +++ b/src/github_actions/common/actions/init_project.py @@ -51,6 +51,7 @@ def run(self) -> str: rmk_config = json.loads(output) return rmk_config["config"]["Tenant"] + class GetRootDomain(BaseCommand, CMDInterface): def __init__(self, environment: str): super().__init__(environment)