Skip to content

Commit

Permalink
Merge pull request #196 from ZLLentz/fix_ioc_deploy_casing
Browse files Browse the repository at this point in the history
FIX: issues with ioc-deploy casing
  • Loading branch information
ZLLentz authored Sep 4, 2024
2 parents 3f5e951 + 0540aad commit 1ab5b21
Showing 1 changed file with 105 additions and 4 deletions.
109 changes: 105 additions & 4 deletions scripts/ioc_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import sys
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Tuple

EPICS_SITE_TOP_DEFAULT = "/cds/group/pcds/epics"
GITHUB_ORG_DEFAULT = "pcdshub"
Expand Down Expand Up @@ -133,7 +134,10 @@ def main(args: CliArgs) -> int:

logger.info("Running ioc-deploy: checking inputs")
upd_name = finalize_name(
name=args.name, github_org=args.github_org, verbose=args.verbose
name=args.name,
github_org=args.github_org,
ioc_dir=args.ioc_dir,
verbose=args.verbose,
)
upd_rel = finalize_tag(
name=upd_name,
Expand Down Expand Up @@ -171,11 +175,11 @@ def main(args: CliArgs) -> int:
return ReturnCode.SUCCESS


def finalize_name(name: str, github_org: str, verbose: bool) -> str:
def finalize_name(name: str, github_org: str, ioc_dir: str, verbose: bool) -> str:
"""
Check if name is present in org and is well-formed.
Check if name is present in org, is well-formed, and has correct casing.
If the name is present, return it.
If the name is present, return it, fixing the casing if needed.
If the name is not present and the correct name can be guessed, guess.
If the name is not present and cannot be guessed, raise.
Expand All @@ -187,6 +191,9 @@ def finalize_name(name: str, github_org: str, verbose: bool) -> str:
However, "ads-ioc" will resolve to "ioc-common-ads-ioc".
Only common IOCs will be automatically discovered using this method.
Note that GitHub URLs are case-insensitive, so there's no native way to tell
from a clone step if you've maintained the correct casing information.
"""
split_name = name.split("-")
if len(split_name) < 3 or split_name[0] != "ioc":
Expand All @@ -203,9 +210,103 @@ def finalize_name(name: str, github_org: str, verbose: bool) -> str:
raise ValueError(
f"Error cloning repo, make sure {name} exists in {github_org} and check your permissions!"
) from exc
readme_text = ""
# Search for readme in any casing with any file extension
pattern = "".join(f"[{char.lower()}{char.upper()}]" for char in "readme") + "*"
for readme_path in (Path(tmpdir) / name).glob(pattern):
with open(readme_path, "r") as fd:
readme_text += fd.read()
logger.debug("Successfully read repo readme for backup casing check")
if not readme_text:
logger.debug("Unable to read repo readme for backup casing check")
logger.debug("Checking deploy area for casing")
# GitHub URLs are case-insensitive, so we need further checks
# REST API is most reliable but requires different auth
# Checking existing directories is ideal because it ensures consistency with earlier releases
# Check the readme last as a backup
repo_dir = Path(
get_target_dir(name=name, ioc_dir=ioc_dir, release="placeholder")
).parent
if repo_dir.exists():
logger.debug(f"{repo_dir} exists, using as-is")
return name
logger.info(f"{repo_dir} does not exist, checking for other casings")
_, area, suffix = split_ioc_name(name)
# First, check for casing on area
found_area = False
for path in Path(ioc_dir).iterdir():
if path.name.lower() == area.lower():
area = path.name
found_area = True
logger.info(f"Using {area} as the area")
break
if not found_area:
logger.info("This is a new area, checking readme for casing")
name = casing_from_readme(name=name, readme_text=readme_text)
logger.info(f"Using casing: {name}")
return name

found_suffix = False
for path in (Path(ioc_dir) / area).iterdir():
if path.name.lower() == suffix.lower():
suffix = path.name
found_suffix = True
logger.info(f"Using {suffix} as the name")
break
if not found_suffix:
logger.info("This is a new ioc, checking readme for casing")
# Use suffix from readme but keep area from directory search
suffix = split_ioc_name(casing_from_readme(name=name, readme_text=readme_text))[2]

name = "-".join(("ioc", area, suffix))
logger.info(f"Using casing: {name}")
return name


def split_ioc_name(name: str) -> Tuple[str, str, str]:
"""
Split an IOC name into ioc, area, suffix
"""
return tuple(name.split("-", maxsplit=2))


def casing_from_readme(name: str, readme_text: str) -> str:
"""
Returns the correct casing of name in readme_text if available.
If this isn't available, check for the suffix in readme_text and use it.
This helps for IOCs that only have the suffix in the readme.
If neither is available, return the original name and log some warnings.
"""
try:
return casing_from_text(uncased=name, casing_source=readme_text)
except ValueError:
...
_, area, suffix = split_ioc_name(name)
try:
new_suffix = casing_from_text(uncased=suffix, casing_source=readme_text)
except ValueError:
logger.warning(
"Did not find any casing information in readme. Please double-check the name!"
)
return name
logger.warning(
"Did not find area casing information in readme. Please double-check the name!"
)
return f"ioc-{area}-{new_suffix}"


def casing_from_text(uncased: str, casing_source: str) -> str:
"""
Returns the casing of the uncased text as found in casing_source.
Raises ValueError if this fails.
"""
index = casing_source.lower().index(uncased.lower())
return casing_source[index : index + len(uncased)]


def finalize_tag(name: str, github_org: str, release: str, verbose: bool) -> str:
"""
Check if release is present in the org.
Expand Down

0 comments on commit 1ab5b21

Please sign in to comment.