This repository has been archived by the owner on Jan 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e7c6d74
commit c833add
Showing
1 changed file
with
152 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,23 @@ | |
import json | ||
import itertools | ||
from collections import defaultdict | ||
# from rich import console | ||
|
||
# Typer CLI Info | ||
app = typer.Typer() | ||
info_app = typer.Typer() | ||
app.add_typer(info_app, name="info") | ||
repos_app = typer.Typer() | ||
app.add_typer(repos_app, name="repos") | ||
|
||
# Configure Logging | ||
logger = logging.getLogger(__name__) | ||
logger.setLevel(logging.INFO) | ||
file_handler = logging.FileHandler("update_info.log") | ||
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') | ||
file_handler.setFormatter(formatter) | ||
# Add the file handler to the logger | ||
logger.addHandler(file_handler) | ||
|
||
with open("cookiecutter.json") as f: | ||
data = json.load(f) | ||
|
@@ -23,7 +40,13 @@ | |
|
||
|
||
def get_azure_combinations() -> Generator[tuple[str, str], None, None]: | ||
"""Returns the base_keys and base_values for the combinations""" | ||
""" | ||
Returns the base_keys and base_values for the combinations | ||
Yields: | ||
tuple[str, str]: The base_keys and base_values for the combinations | ||
Example: ("azure-flask-postgresql-azure-app-service", "Flask PostgreSQL Azure App Service") | ||
""" | ||
for framework, db_resource, host in combos: | ||
base_keys = ( | ||
(f"azure-{framework[0]}/{db_resource[0]}/{host[0]}") | ||
|
@@ -37,13 +60,15 @@ def get_azure_combinations() -> Generator[tuple[str, str], None, None]: | |
yield base_keys, base_values | ||
|
||
|
||
app = typer.Typer() | ||
logger = logging.getLogger(__name__) | ||
logger.setLevel(logging.DEBUG) | ||
logger.FileHandler("update_info.log") | ||
|
||
@app.command() | ||
@info_app.command(name='list_repos') | ||
def metadata_list(): | ||
""" | ||
Creates a json file with the metadata for the combinations | ||
TODO: #302 Allow passing in a file to update_repos to iterate | ||
TODO: #303 Add a pattern that will allow for updating a subset of repos | ||
TODO: #304 Create a list of patterns to exclude | ||
""" | ||
metadata_dict = defaultdict(list) | ||
|
||
for framework, db_resource, host in combos: | ||
|
@@ -60,90 +85,9 @@ def metadata_list(): | |
with open("metadata.json", "w") as outfile: | ||
json.dump(metadata_dict, outfile, indent=4) | ||
|
||
|
||
@app.command() | ||
def update_all_repos(): | ||
""" | ||
Iterates through combinations: | ||
- Creates a Placeholder Folder | ||
- Attempts to pull the combination url | ||
- Runs Cruft to update based on the path's cruft.json | ||
- Creates pull request for the updates | ||
""" | ||
|
||
logger.debug("Creating Placeholder Folder") | ||
base_path = pathlib.Path(f"update_repos/{random_cc_folder_prefix}") | ||
base_path.mkdir(parents=True, exist_ok=True) | ||
|
||
logger.debug("Cloning Repos and Running Cruft") | ||
for base_key, base_values in get_azure_combinations(): | ||
base_file = base_key.replace("-", "_") | ||
url = f"[email protected]:Azure-Samples/{base_key}.git" | ||
cmd = ["git", "clone", url, base_file] | ||
path = base_path.joinpath(base_file) | ||
|
||
try: | ||
subprocess.check_output( | ||
cmd, | ||
cwd=base_path, | ||
) | ||
|
||
except subprocess.CalledProcessError as e: | ||
logger.warning(f"Could not to clone {url}: {e}. This is likely a non-existent repo.") | ||
continue | ||
|
||
branch_name = f"cruft/update" | ||
subprocess.check_output( | ||
["git", "checkout", "-b", branch_name], | ||
text=True, | ||
cwd=path, | ||
) | ||
|
||
cruft.update( | ||
path, skip_apply_ask=True, extra_context={"python_version": "3.12"} | ||
) | ||
|
||
if not subprocess.check_output( | ||
["git", "status", "--porcelain"], | ||
text=True, | ||
cwd=path, | ||
): | ||
logger.info(f"No changes for {base_file}, skipping.") | ||
continue | ||
|
||
if subprocess.check_output( | ||
["ls -a" "**/*.rej"], | ||
text=True, | ||
cwd=path, | ||
): | ||
logger.error(f"Rejection files found for {base_file}!") | ||
exit(1) | ||
|
||
subprocess.check_output( | ||
["git", "add", "."], | ||
text=True, | ||
cwd=path, | ||
) | ||
subprocess.check_output( | ||
["git", "commit", "-m", "Cruft Update"], | ||
text=True, | ||
cwd=path, | ||
) | ||
subprocess.check_output( | ||
["git", "push", "--set-upstream", "origin", branch_name], | ||
text=True, | ||
cwd=path, | ||
) | ||
subprocess.check_output( | ||
["gh", "pr", "create", "--fill", "--reviewer", "kjaymiller,pamelafox"], | ||
text=True, | ||
cwd=path, | ||
) | ||
|
||
|
||
@app.command() | ||
@info_app.command() | ||
def update_readme(): | ||
"""Updates the README of cookiecutter-relecloud with the list of combinations""" | ||
web_framework_values = defaultdict(list) | ||
|
||
for framework, db_resource, host in combos: | ||
|
@@ -168,6 +112,124 @@ def update_readme(): | |
|
||
print(f"{len(list(combos))}: Total Combinations") | ||
|
||
def create_base_folder(): | ||
"""Creates the base folder for the repo""" | ||
base_path = pathlib.Path(f"update_repos/{random_cc_folder_prefix}") | ||
base_path.mkdir(parents=True, exist_ok=True) | ||
return base_path | ||
# TODO: Get repos by pattern | ||
|
||
|
||
def get_repos_by_pattern(pattern:str, repos: list[str]=list(get_azure_combinations())) -> list[str]: | ||
""" | ||
Returns a list of repos that match the provided pattern. | ||
TODO: #305 Add Test | ||
""" | ||
pattern = re.compile(pattern) | ||
matching_repos = [repo for repo in repos if pattern.match(repo)] | ||
return matching_repos | ||
|
||
def update_repo(repo:str, path: pathlib.Path, branch:str="cruft/update", **kwargs) -> None: | ||
""" | ||
Updates the repo with the provided name | ||
Parameters: | ||
repo (str): The name of the repo to update. | ||
It should be the same as seen on GitHub. | ||
path (pathlib.Path): The parent folder where the will be saved. | ||
branch (str): The name of the branch to create and push to. | ||
Defaults to cruft/update. | ||
**kwargs: Additional keyword arguments to pass to cruft.update as | ||
""" | ||
# console = console.Console() | ||
logger.debug("Creating Placeholder Folder") | ||
url = f"[email protected]:Azure-Samples/{repo}.git" | ||
path = path.joinpath(repo) | ||
|
||
try: | ||
subprocess.check_output( | ||
["git", "clone", url], | ||
cwd=path.parent, | ||
) | ||
|
||
except subprocess.CalledProcessError as e: | ||
raise ValueError(f"Could not to clone {url}: {e}.\nThis is likely a non-existent repo.") | ||
|
||
subprocess.check_output( | ||
["git", "checkout", "-b", branch], | ||
text=True, | ||
cwd=path, | ||
) | ||
|
||
cruft.update( | ||
path, skip_apply_ask=True, extra_context=kwargs, | ||
) | ||
|
||
if not subprocess.check_output( | ||
["git", "status", "--porcelain"], | ||
text=True, | ||
cwd=path, | ||
): | ||
logger.info(f"No changes for {path}, skipping.") | ||
return | ||
|
||
if rejection_files:=list(pathlib.Path(path).rglob("*.rej")): | ||
logger.error(f"Rejection files found for {path}!\nFiles: {'\n- '.join(rejection_files)}") | ||
exit(1) | ||
|
||
# Add the Changes and Create a PR | ||
subprocess.check_output( | ||
["git", "add", "."], | ||
text=True, | ||
cwd=path, | ||
) | ||
subprocess.check_output( | ||
["git", "commit", "-m", "Cruft Update"], | ||
text=True, | ||
cwd=path, | ||
) | ||
subprocess.check_output( | ||
["git", "push", "--set-upstream", "origin", branch], | ||
text=True, | ||
cwd=path, | ||
) | ||
subprocess.check_output( | ||
["gh", "pr", "create", "--fill", "--reviewer", "kjaymiller,pamelafox"], | ||
text=True, | ||
cwd=path, | ||
) | ||
|
||
@repos_app.command(name="update-one") | ||
def update_single_repo(repo:str, branch:str="cruft/update") -> None: | ||
"""Clones a single repo and updates it using `cruft update`""" | ||
logger.info(f) | ||
return update_repo(repo=repo, path=create_base_folder(), branch=branch) | ||
|
||
@repos_app.command(name="update-many") | ||
def update_repos(pattern:str, branch:str="cruft/update") -> None: | ||
"""Updates all repos that match the provided pattern""" | ||
path = create_base_folder() | ||
repos = get_repos_by_pattern(pattern) | ||
for repo in repos: | ||
update_repo(repo=repo, path=path, branch=branch) | ||
|
||
|
||
@repos_app.command(name="update-all") | ||
def update_all_repos(branch:str="cruft/update"): | ||
""" | ||
Iterates through combinations: | ||
- Creates a Placeholder Folder | ||
- Attempts to pull the combination url | ||
- Runs Cruft to update based on the path's cruft.json | ||
- Creates pull request for the updates | ||
""" | ||
|
||
path = create_base_folder() | ||
for base_key,_ in get_azure_combinations(): | ||
update_repo(base_key, path, branch=branch) | ||
|
||
|
||
|
||
if __name__ == "__main__": | ||
app() |