Skip to content

Instantly share code, notes, and snippets.

@Sigmanificient
Last active August 9, 2024 20:12
Show Gist options
  • Save Sigmanificient/c68e03d1ad750a83233c6ab05761cbf2 to your computer and use it in GitHub Desktop.
Save Sigmanificient/c68e03d1ad750a83233c6ab05761cbf2 to your computer and use it in GitHub Desktop.
Script to insert missing `preInstall` and `postInstall` hook within `installPhase` of nixpkgs derivations
import io
import re
import pathlib
import sys
HOOK_PATTERN = r'(?P<ident>[ ]+)?(?P<decl>installPhase\s+=\s+)"(?P<block>.*)";'
HOOK_PATTERN_MULTILINE = (
r"(?P<decl>installPhase\s+=\s+(?:let(?:(?!\s+in\s+)(?:.|\n))+\s+in\s+)?)"
r"'{2,3}\n?(?P<ident>\s+)?"
r"(?P<block>(?:(?!'{2,3};)(?:\n|.)?)+)"
r"(?P<quotes>'{2,3});"
)
def _replace_match_helper(pattern, func):
def wrapped(content: str):
patched = io.StringIO()
last = 0
hit = 0
for matched in re.finditer(pattern, content):
full = matched.group()
if "preInstall" in full or "postInstall" in full:
continue
prev, post = matched.span()
patched.write(content[last:prev])
func(patched, matched)
last = post
hit += 1
patched.write(content[last:])
return patched.getvalue(), hit
return wrapped
def replace_match(pattern):
def wrapper(func):
return _replace_match_helper(pattern, func)
return wrapper
@replace_match(HOOK_PATTERN)
def add_runhook(out: io.StringIO, matched: re.Match) -> bool:
ident, decl, block = matched.groups()
ident_out = ident or ''
ident = f"{ident_out} "
# block = f'\n{ident}'.join(line.strip() for line in block.split(';'))
for part in (
ident_out, decl,
f'"runHook preInstall; {block}; runHook postInstall";'
):
out.write(part)
return True
@replace_match(HOOK_PATTERN_MULTILINE)
def add_runhook_multiline(out: io.StringIO, matched: re.Match):
decl, ident, block, quotes = matched.groups()
ident = ident or ' '
post_ident = ' ' * (len(ident) - 2)
for part in (
decl, f"{quotes}\n",
ident, "runHook preInstall\n\n",
ident, block.strip(), "\n\n",
ident, "runHook postInstall\n",
post_ident, f"{quotes};",
):
out.write(part)
return True
def main():
hits = 0
for filepath in (line.rstrip("\r\n") for line in sys.stdin):
content = pathlib.Path(filepath).read_text()
print("processing:", filepath)
patched, hit = add_runhook(content)
hits += hit
patched, hit = add_runhook_multiline(patched)
hits += hit
with open(filepath, 'w+') as f:
f.write(patched)
print(hits)
if __name__ == "__main__":
main()
grep -rP "installPhase =" \
| cut -d ':' -f 1 \
| xargs -i sh -c "grep -P 'runHook\s+preInstall' {} >/dev/null || echo '{}'" \
| python add_missing_hooks.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment