Skip to content

dotenv

backup_and_update_dotfiles(etcpath, oldetc, configs)

Updates dotfiles

Source code in hpcman/hpcman/user/dotenv.py
def backup_and_update_dotfiles(etcpath: Path, oldetc: str, configs: list[ConfigFiles]) -> None:
    """Updates dotfiles"""
    if not check_etcpath(etcpath):
        logger.error("Please provide a different path to look for dotfiles.")
        exit(1)

    for conf in configs:
        if (rc := Path.home() / conf.value).exists():
            backup = make_backup(rc.as_posix())
            match conf:
                case ConfigFiles.BASHRC:
                    stdrc = "std.bashrc"
                    copyln = f"cp {INITSDIR}/bashrc ~/.bashrc"
                case ConfigFiles.CSHRC:
                    stdrc = "std.cshrc"
                    copyln = f"cp {INITSDIR}/cshrc ~/.cshrc"
                case ConfigFiles.ZSHRC:
                    stdrc = "std.zshrc"
                    copyln = f"cp {INITSDIR}/zshrc ~/.zshrc"

            count = 0
            replace = 0

            with fileinput.input(rc, inplace=True) as fh:
                for ln, line in enumerate(fh):
                    if stdrc in line:
                        count += 1
                        if oldetc in line:
                            logger.info(f"Updating line {ln}: {line.strip()}")
                            line = line.replace(oldetc, etcpath.as_posix())
                            replace += 1
                        elif (etc_str := etcpath.as_posix()) in line:
                            logger.warning(f"New etc path {etc_str} already found on line {ln}.")
                    print(line, end="")

            if replace == 0:
                logger.info(f"No lines updated in {rc.as_posix()}. Did you already update {conf.value}?")
                logger.info(f"old etc -> {oldetc}; new etc -> {etcpath.as_posix()}")
                logger.log("HINT", "If you think you need a new config file, you can run the below command to get one:")
                logger.log("HINT", copyln)
                logger.log("HINT", f"You can view your backup {backup} to copy any additional changes over.")
                logger.log(
                    "HINT",
                    f"Alternatively, you can manually update your dotfile to point to {etcpath.as_posix()}/{stdrc}",
                )
            elif count == 0:
                logger.warning(f"No {stdrc} found in {rc.as_posix()}.")
                logger.warning(f"Did you previously make custom changes to your {stdrc} file?")
                logger.error(f"Unable to update {rc.as_posix()} as {stdrc} is not found. Manual intervention required.")
            elif count == replace:
                logger.info(f"Updated {replace} lines in {rc.as_posix()}.")
            else:
                logger.warning(f"Updated {replace} lines in {rc.as_posix()} with {stdrc} found on {count}.")
                logger.warning("Check the config file before continuing.")
                logger.warning(f"Consider getting the backup from {backup.as_posix()}")
        else:
            logger.warning(f"Skipping {conf.value} as it does not exist.")

    print_reload_message()

check_etcpath(etcpath)

Checks for expected files in etcpath.

Source code in hpcman/hpcman/user/dotenv.py
def check_etcpath(etcpath: Path) -> bool:
    """Checks for expected files in etcpath."""
    filenames = ("std.bashrc", "std.cshrc", "std.zshrc")
    check = True
    for fn in filenames:
        if not Path(etcpath / fn).exists():
            logger.error(f"{fn} is missing from {etcpath.as_posix()}")
            check = False
    return check

make_backup(fn)

Makes backup of provided file.

Source code in hpcman/hpcman/user/dotenv.py
def make_backup(fn: str | Path) -> Path:
    """Makes backup of provided file."""
    backup = Path(fn)
    i = 1
    while backup.exists() and i <= 10:
        backup = backup.with_suffix(f".bak{i}")
        i += 1

    logger.info(f"Backup copy of {fn} is here: {copyfile(fn, backup)}")
    return backup

run_gmes_setup()

Runs gmes setup script to link gmes key in home directory

Source code in hpcman/hpcman/user/dotenv.py
def run_gmes_setup() -> None:
    """Runs gmes setup script to link gmes key in home directory"""
    run([GMESSETUP], check=False)

set_augustus()

Updates AUGUSTUS_CONFIG_PATH to value within the HPCMAN_USER_PREFIX directory.

Source code in hpcman/hpcman/user/dotenv.py
def set_augustus() -> None:
    """Updates AUGUSTUS_CONFIG_PATH to value within the HPCMAN_USER_PREFIX directory."""
    logger.trace(f"Pulling {VARUSERPREFIX} from {VARENVFILE}.")
    if (value := get_key(environ[VARENVFILE], VARUSERPREFIX)) is not None:
        if (prefix := Path(value)).exists():
            augustus_prefix = prefix / "augustus"
            logger.info(f"Making augustus directory {augustus_prefix.as_posix()}")
            augustus_prefix.mkdir(mode=0o755, exist_ok=True)
            logger.info("Copying default augustus config directory")
            config_path = augustus_prefix / "config"
            if config_path.exists():
                logger.warning(f"augustus config directory already exists {config_path}")
                logger.warning("Did you already set up augustus?")
                logger.log("HINT", "Remove the config dir if you want to make a new copy.")
                exit(1)
            result = copytree(AUGUSTUSCONFIG, config_path)
            if Path(result).exists():
                logger.success(f"Successfully copied augustus config to {result}")
                set_key(environ[VARENVFILE], "AUGUSTUS_CONFIG_PATH", result.as_posix(), export=True)
                update_csh_envfile()
        else:
            logger.error(f"Provided user prefix {value} does not exist.")
            logger.error("Rerun 'hpcman user setup prefix' and try again.")
            exit(1)
    else:
        prefix_error()

    logger.info("Setting up genemark key file")
    run_gmes_setup()

    if (value := get_key(environ[VARENVFILE], "AUGUSTUS_CONFIG_PATH")) is not None:
        logger.success(f"Successfully set up AUGUSTUS_CONFIG_PATH={value}")
        print_reload_message()
    else:
        logger.critical("Unable to set AUGUSTUS_CONFIG_PATH properly!")
        logger.critical(f"Check your env var {VARENVFILE} and your permissions for the file listed, if present.")

set_default_channels(pixi_home, pixi_cache, cpuarch)

Sets default pixi channels in the pixi config.toml file.

Source code in hpcman/hpcman/user/dotenv.py
def set_default_channels(pixi_home: str, pixi_cache: str, cpuarch: CPUArch) -> None:
    """Sets default pixi channels in the pixi config.toml file."""
    new_env = environ.copy()
    new_env["PIXI_HOME"] = pixi_home
    new_env["PIXI_CACHE_DIR"] = pixi_cache
    run(["pixi", "config", "unset", "-g", "default-channels"], text=True, check=True, env=new_env)
    if cpuarch == CPUArch.IBM:
        run(["pixi", "config", "append", "-g", "default-channels", "https://ftp.osuosl.org/pub/open-ce/current"], text=True, check=True, env=new_env)
    run(["pixi", "config", "append", "-g", "default-channels", "conda-forge"], text=True, check=True, env=new_env)
    run(["pixi", "config", "append", "-g", "default-channels", "bioconda"], text=True, check=True, env=new_env)

set_rlibs()

Updates R_LIBS_USER to value within the HPCMAN_USER_PREFIX directory.

Source code in hpcman/hpcman/user/dotenv.py
def set_rlibs() -> None:
    """Updates R_LIBS_USER to value within the HPCMAN_USER_PREFIX directory."""
    logger.trace(f"Pulling {VARUSERPREFIX} from {VARENVFILE}.")
    renviron = Path(Path.home() / ".Renviron").as_posix()
    if (value := get_key(environ[VARENVFILE], VARUSERPREFIX)) is not None:
        r_libs_user = f"{value}/R/%p-library/%v"
        logger.trace(r_libs_user)
        logger.log("HINT", "R_LIBS_USER is set in ~/.Renviron for you.")
        logger.log("HINT", "If you want to manually change it, you'll have to do it there.")
        logger.log("HINT", "To temporarily disable the R_LIBS_USER for an R session, you can use 'R --no-environ'")
        set_key(renviron, "R_LIBS_USER", r_libs_user, export=False)
    else:
        prefix_error()

    if (value := get_key(renviron, "R_LIBS_USER")) is not None:
        logger.success(f"Successfully set up R_LIBS_USER={value}")
        retval = generate_rlibs_dir(r_libs_user=value)
        if retval is None:
            pass
        elif not retval:
            logger.log("HINT", "Use 'hpcman user setup R' to get a directory generated using the new value.")
    else:
        logger.critical("Unable to set R_LIBS_USER properly!")
        logger.critical(f"Check your env var {VARENVFILE} and your permissions for the file listed, if present.")

setup_envfile(config)

Enables hpcman to update hpcman-managed config files for users.

Source code in hpcman/hpcman/user/dotenv.py
def setup_envfile(config: Path) -> None:
    """Enables hpcman to update hpcman-managed config files for users."""
    if environ.get(VARENVFILE):
        logger.error("Your shell is already configured using hpcman.")
        logger.error(
            f"unset {VARENVFILE} and remove the old source env line from your bashrc and cshrc to re-set your shell."
        )
        exit(1)

    envfile = Path(config / "env.sh")
    envfile_str = envfile.resolve().as_posix()
    configline = "## hpcman config; Added by hpcman user enable"
    source = f"source {quote(envfile_str)}"

    envfile_csh = Path(config / "env.csh")
    envfile_csh_str = envfile_csh.resolve().as_posix()
    source_csh = f"source {quote(envfile_csh_str)}"

    if not config.exists():
        logger.trace(f"{config} does not exist.")
        if Confirm.ask(f"Dir '{config}' does not exist. Create it?", default=True):
            config.mkdir(mode=0o750, parents=True, exist_ok=True)

    if not config.exists():
        logger.error(f"Config dir {config} does not exist.")
        logger.error("Make the directory or specify another before continuing.")
        exit(1)
    else:
        logger.trace(f"Making envfiles {envfile_str}, {envfile_csh_str}.")
        envfile.touch(mode=0o640, exist_ok=True)
        logger.info(f"Setting {VARENVFILE} in envfile to enable configuration.")
        set_key(envfile, VARENVFILE, envfile_str, export=True)
        set_key(envfile, VARENVCSHFILE, envfile_csh_str, export=True)
        update_csh_envfile(envfile_str, envfile_csh_str)

    if get_key(envfile, VARENVFILE) is not None:
        logger.success("hpcman envfile created.")
        logger.success(
            f"Add {source} to your ~/.bashrc or {source_csh} to your ~/.cshrc and restart your shell to finalize the "
            "setup."
        )
        logger.success("Copy/paste the below line into your terminal for this to work automatically:")
        print(f"\necho {quote(source)} >> ~/.bashrc")
        print(f"echo {quote(source_csh)} >> ~/.cshrc\n")
        if Confirm.ask(
            "I can also add the line to your configs for you automatically, would you like that?", default=False
        ):
            for rcfn in (".bashrc", ".cshrc", ".zshrc"):
                rc = Path(Path.home() / rcfn)
                logger.debug(f"Opening {rc} for writing.")
                if not rc.exists():
                    logger.warning(f"Specified config file {rcfn} does not exist. Skipping...")
                    continue
                with rc.open("r+t") as fh:
                    logger.trace("Searching for line already present in file")
                    for i, line in enumerate(fh, start=1):
                        if line.strip() == configline:
                            logger.error(f"hpcman config line already detected on line {i}")
                            logger.error(f"Clean your {rcfn} before continuing.")
                            exit(1)
                    match rcfn:
                        case ".bashrc":
                            sourceline = source
                        case ".cshrc":
                            sourceline = source_csh
                        case ".zshrc":
                            sourceline = source
                    logger.trace(f"Adding {sourceline} to {rcfn}")
                    fh.write(f"\n{configline}\n")
                    fh.write(f"{sourceline}\n\n")

                logger.success(f"Here is your updated {rcfn} file:")
                console = Console()
                console.print(Syntax.from_path(rc.as_posix(), line_numbers=True))

        print_reload_message()
    else:
        logger.critical(f"The {VARENVFILE} setting does not seem to be present in {envfile}")
        logger.critical("hpcman cannot continue with configuration of your shell. Check your settings/permissions.")