Skip to content

Instantly share code, notes, and snippets.

@nyurik
Last active May 31, 2025 04:55
Show Gist options
  • Save nyurik/8a6db6bfcd0eb7de52287071e63c8ac5 to your computer and use it in GitHub Desktop.
Save nyurik/8a6db6bfcd0eb7de52287071e63c8ac5 to your computer and use it in GitHub Desktop.
A script to sort justfile by recipe name
#!/usr/bin/env python3
"""
A script to sort justfile entries alphabetically and move private recipes to the end.
Current limitations: fails if there is more than one recipe with an identical name (e.g. for multiple architectures)
"""
import re
import sys
def sort_justfile(file_path):
with open(file_path, 'r') as file:
lines = file.readlines()
recipes = {}
pending = []
found_recipe = ''
for line in lines:
matches = re.match(r'^@?([-_a-zA-Z]+)( [^:]*)?\s*:($|[^=])', line)
if matches:
tmp = [] # any strings that are part of the new recipe
is_private = False
for idx in range(len(pending) - 1, -1, -1):
l = pending[idx]
if (l.startswith('#') or l.startswith('[')) and not l.startswith('#!'):
tmp.insert(0, pending.pop())
if l.startswith('[') and 'private' in l:
is_private = True
continue
break
recipes[found_recipe] = pending
found_recipe = matches.group(1)
if is_private:
found_recipe = f'zzzz {found_recipe}'
assert found_recipe not in recipes, f"Found recipe '{found_recipe}' twice in {file_path}"
pending = tmp
pending.append(line)
if pending:
recipes[found_recipe] = pending
with open(file_path, 'w') as file:
for recipe in sorted(recipes.keys()):
if recipe:
print(f"Writing recipe: {recipe}")
file.write('\n')
# remove empty lines at the beginning and the end of the recipe
lines = recipes[recipe]
while lines and not lines[0].strip():
lines.pop(0)
while lines and not lines[-1].strip():
lines.pop()
for line in lines:
file.write(line)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: sort-justfile.py <justfile>")
sys.exit(1)
sort_justfile(sys.argv[1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment