fix_tfvars_symlinks.py (137 lines of code) (raw):
import os
import re
import sys
import shutil
# Problem : when git cloning from github symlinks become text in files
# e.g. 3-networks-hub-and-spoke\envs\development\common.auto.tfvars : ../../common.auto.tfvars
# not only .auto.tfvars but also .tfvars
# to find them use regex r'^[\./]+[^\W\./]+\.auto\.tfvars' or better r'^[\./]+[^\n]+\.tfvars'
# this should have been a symlink. If on Windows symlinks not available
# if the referenced file does not exist most likely because we have examples something like
# access_context.auto.example.tfvars, common.auto.example.tfvars, etc
# however if git-cloned on Linux the symlinks remain symlinks and the script will only fix the broken ones
# the fix consists in creating thje missing file referenced by the symlink with a text ### fill-me-in with tfvars definitions
# list of extensions to replace
# DEFAULT_REPLACE_EXTENSIONS = None
# example: uncomment next line to only replace *.c, *.h, and/or *.txt
# DEFAULT_REPLACE_EXTENSIONS = (".c", ".h", ".txt")
DEFAULT_TFVAR_EXTENSIONS = (".tfvars")
regex_find_symlink = r'^[\./]+([^\n/]+)\.tfvars'
regex_find_mod_tfvars = r'^([^\n\./]+(\.auto|))\.mod(\.tfvars)'
re_repl_mod_tfvars = r'\1\3'
regex_find_tfvars = r'^([^\n\./]+(\.auto|))\.tfvars'
fixed_fake_symlinked_count = 0
fixed_tfvars_symlinked_count = 0
fix_symlink_error_count = 0
# pat_replace = list( ([re.compile(one_re_find_repl[0]),one_re_find_repl[1]] for one_re_find_repl in regex_find_replace))
pat_find_symlink = re.compile(regex_find_symlink)
pat_find_mod_tfvars = re.compile(regex_find_mod_tfvars)
pat_find_tfvars = re.compile(regex_find_tfvars)
def check_file_type(fname, tfvar_extensions=DEFAULT_TFVAR_EXTENSIONS):
if tfvar_extensions:
return fname.lower().endswith(tfvar_extensions)
return False
## do not overwrite destination if exists
def create_symlink_or_hardcopy(src_file_path, dst_file_path):
if os.path.exists(dst_file_path):
return (dst_file_path,"exists")
try:
os.symlink(src_file_path, dst_file_path)
return (dst_file_path,"symlink")
except:
print("Could not symlink {} to {}".format(src_file_path,dst_file_path))
try:
shutil.copy2(src_file_path,dst_file_path)
return (dst_file_path,"hardlink")
except:
print("Could not copy {} to {}".format(src_file_path,dst_file_path))
return None
# if a <fname>.mod.tfvars file exists but not a <fname>.tfvars file, a symlink or hardcopy is created
# first a symlink creation attempted, if it fails a hardcopy is created
def create_mod_tfvars_symlink_or_hardcopy(fname, dirpath):
tfvars_full_path = None
tfvars_mod_full_path = None
if re.search(pat_find_mod_tfvars,fname):
tfvars_fname = re.sub(pat_find_mod_tfvars,re_repl_mod_tfvars,fname)
tfvars_full_path = os.path.join(dirpath,tfvars_fname)
tfvars_mod_full_path = os.path.join(dirpath,fname)
if (tfvars_full_path is None):
return None
result = create_symlink_or_hardcopy(tfvars_mod_full_path,tfvars_full_path)
return result
## looks for <name>.mod.tfvars files and checks if a symlink exists to them in same folder
## if no symlink, will either create one or (Windows non-admin) will simply copy the file
##
def fix_mod_tfvars_symlinks(src_fname, dirpath):
global fixed_tfvars_symlinked_count
global fix_symlink_error_count
# normalize just in case if src_fname is a path
fname = os.path.basename(src_fname)
fname_abs_path = os.path.join(dirpath,fname)
# first create symlink to a .mod.tfvars in same folder
if re.match(pat_find_mod_tfvars,fname):
result = create_mod_tfvars_symlink_or_hardcopy(fname,dirpath)
if result is None:
print("error symlinking {} in {}".format(fname,dirpath))
fix_symlink_error_count += 1
elif re.match(r'exists',result[1]):
## for some reason called twice on same file
print("error symlinking {} in {}, destination {} already exists".format(fname,dirpath,result[0]))
## silently ignore if dest file already exists
return
elif re.match(r'symlink',result[1]):
print("symlinked {} in {} to {}".format(fname,dirpath,result[0]))
fixed_tfvars_symlinked_count += 1
elif re.match(r'hardlink',result[1]):
print("copied {} in {} to {}".format(fname,dirpath,result[0]))
fixed_tfvars_symlinked_count += 1
def fix_tfvars_symlinks(src_fname, dirpath):
global fixed_fake_symlinked_count
global fix_symlink_error_count
global pat_find_symlink
# normalize just in case if src_fname is a path
fname = os.path.basename(src_fname)
fname_abs_path = os.path.join(dirpath,fname)
if re.match(pat_find_tfvars,fname):
if not os.path.islink(fname_abs_path):
tfvars_path_above = None
symlink_path = None
lines = None
try:
with open(fname_abs_path,'r') as f:
lines = f.readlines()
f.close()
except:
print("can't open file {}".format(fname_abs_path))
fix_symlink_error_count += 1
return
if len(lines) == 1 and re.match(pat_find_symlink, lines[0]):
symlink_path = lines[0]
tfvars_path_above = os.path.join(dirpath,symlink_path)
else:
return
# exit if error
if tfvars_path_above is None or not os.path.exists(tfvars_path_above):
print("missing source tfvars file" + "" if tfvars_path_above is None else tfvars_path_above)
fix_symlink_error_count += 1
return
# rename source file and try to create symlink, otherwise hard copy
print("renaming " + fname_abs_path + "\n to " + fname_abs_path + ".delete_me")
os.rename(fname_abs_path, fname_abs_path +'.delete_me')
if create_symlink_or_hardcopy(tfvars_path_above,fname_abs_path) is not None:
print("symlinked " + tfvars_path_above + "\n to " + fname_abs_path)
fixed_fake_symlinked_count += 1
else:
fix_symlink_error_count += 1
print("error symlinking " + tfvars_path_above + "\n to " + fname_abs_path)
else:
try:
tfvars_path_above = os.readlink(fname_abs_path)
if not os.path.isabs(tfvars_path_above):
tfvars_path_above = os.path.join(dirpath,tfvars_path_above)
if tfvars_path_above is not None and os.path.exists(tfvars_path_above):
return
print("Broken symlink {} to {}".format(fname_abs_path,tfvars_path_above) if tfvars_path_above is not None else "" )
print("Broken symlink in {}".format(fname_abs_path) if tfvars_path_above is None else "")
except:
print("can't get symlink from file {}".format( fname_abs_path))
fix_symlink_error_count += 1
def get_relative_dir_string(root_dir, current_dir):
abs_root_dir = os.path.abspath(root_dir)
abs_current_dir = os.path.abspath(current_dir)
rel_path = os.path.relpath(abs_current_dir, start=abs_root_dir).replace(os.sep,'/')
return rel_path
def mass_fix_mod_tfvars(root_dir, crt_dir, replace_extensions=DEFAULT_TFVAR_EXTENSIONS):
for dirpath, dirnames, filenames in os.walk(os.path.abspath(crt_dir)):
for fname in filenames:
if check_file_type(fname, replace_extensions):
fullname = os.path.join(dirpath, fname)
fix_mod_tfvars_symlinks(fullname, dirpath)
def mass_fix_symlinks(root_dir, crt_dir, replace_extensions=DEFAULT_TFVAR_EXTENSIONS):
## print("crt_dir="+crt_dir+"\n")
for dirpath, dirnames, filenames in os.walk(os.path.abspath(crt_dir)):
for fname in filenames:
if check_file_type(fname, replace_extensions):
fullname = os.path.join(dirpath, fname)
fix_tfvars_symlinks(fullname, dirpath)
def main(root_dir):
mass_fix_mod_tfvars(root_dir,root_dir)
mass_fix_symlinks(root_dir,root_dir)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: fix_fake_symlinks.py <root_dir>\n")
sys.exit()
main(sys.argv[1])
print("fixed " + str(fixed_fake_symlinked_count) + " fake symlinks and " + str(fixed_tfvars_symlinked_count) + " fixed_tfvars_symlinked_count " + str(fix_symlink_error_count) + " errors" )
## sys.exit(1)
##mass_replace(sys.argv[1])