ci: tag-maintainers extract maintainers in a separate script

Make it easier to test the workflow by moving logic out into separate
script.
This commit is contained in:
Austin Horstman 2025-07-08 21:55:18 -05:00
parent 860754350d
commit 405132bab3
2 changed files with 119 additions and 46 deletions

View file

@ -67,52 +67,9 @@ jobs:
PR_AUTHOR: "${{ github.event.pull_request.user.login }}"
CHANGED_FILES: '${{ steps.changed-files.outputs.changed_files }}'
run: |
if [[ -z "$CHANGED_FILES" ]]; then
echo "No plugin files changed. No maintainers to tag."
echo "maintainers=" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Evaluating nixvim meta system for all maintainers..."
# Get a JSON object mapping plugin paths to maintainer data.
ALL_MAINTAINERS=$(nix eval --impure --expr "
let
nixvim = import ./.;
lib = nixvim.inputs.nixpkgs.lib.extend nixvim.lib.overlay;
emptyConfig = lib.nixvim.evalNixvim {
modules = [ { _module.check = false; } ];
extraSpecialArgs = { pkgs = null; };
};
inherit (emptyConfig.config.meta) maintainers;
in
maintainers
" --json 2>/dev/null || echo "{}")
echo "Finding maintainers for changed files..."
FOUND_MAINTAINERS=$(
echo "$CHANGED_FILES" | while IFS= read -r FILE; do
[[ -z "$FILE" ]] && continue
PLUGIN_DIR=$(dirname "$FILE")
# Use jq to find maintainers whose path key ends with the plugin directory.
echo "$ALL_MAINTAINERS" | jq -r --arg plugindir "$PLUGIN_DIR" '
to_entries[] |
select(.key | endswith($plugindir)) |
.value[] |
select(has("github")) |
.github
' 2>/dev/null
done
)
# De-duplicate the list, remove the PR author, and format as a space-separated string.
MAINTAINERS_LIST=$(echo "$FOUND_MAINTAINERS" | grep -v -w -F "$PR_AUTHOR" | sort -u | tr '\n' ' ' | sed 's/ *$//')
if [[ -z "$MAINTAINERS_LIST" ]]; then
echo "No maintainers found for changed files (or only the PR author is a maintainer)."
else
echo "Found maintainers to notify: $MAINTAINERS_LIST"
fi
MAINTAINERS_LIST=$(./ci/tag-maintainers/extract-maintainers.py \
--changed-files "$CHANGED_FILES" \
--pr-author "$PR_AUTHOR")
echo "maintainers=$MAINTAINERS_LIST" >> "$GITHUB_OUTPUT"

View file

@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""
Extract maintainers from changed plugin files.
This script extracts the maintainer extraction logic from the tag-maintainers workflow
for easier testing and validation.
"""
import argparse
import json
import subprocess
import sys
from typing import List
def run_nix_eval(expr: str) -> str:
"""Run a Nix evaluation expression and return the result."""
try:
result = subprocess.run(
["nix", "eval", "--impure", "--json", "--expr", expr],
capture_output=True,
text=True,
check=True,
)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
print(f"Error running Nix evaluation: {e}", file=sys.stderr)
print(f"Stderr: {e.stderr}", file=sys.stderr)
return "[]"
def extract_maintainers(changed_files: List[str], pr_author: str) -> List[str]:
"""Extract maintainers from changed plugin files."""
if not changed_files:
print("No plugin files changed. No maintainers to tag.", file=sys.stderr)
return []
print("Finding maintainers for changed files...", file=sys.stderr)
changed_files_nix = '[ ' + ' '.join(f'"{f}"' for f in changed_files) + ' ]'
nix_expr = f"""
let
nixvim = import ./.;
lib = nixvim.inputs.nixpkgs.lib;
emptyConfig = nixvim.lib.nixvim.evalNixvim {{
modules = [ {{ _module.check = false; }} ];
extraSpecialArgs.pkgs = null;
}};
inherit (emptyConfig.config.meta) maintainers;
changedFiles = {changed_files_nix};
# Find maintainers for files that match changed plugin directories
relevantMaintainers = lib.concatLists (
lib.mapAttrsToList (path: maintainerList:
let
matchingFiles = lib.filter (file:
lib.hasSuffix (dirOf file) path
) changedFiles;
in
if matchingFiles != [] then maintainerList else []
) maintainers
);
# Extract GitHub usernames
githubUsers = lib.concatMap (maintainer:
if maintainer ? github then [ maintainer.github ] else []
) relevantMaintainers;
in
lib.unique githubUsers
"""
result = run_nix_eval(nix_expr)
try:
maintainers = json.loads(result)
except json.JSONDecodeError:
print(f"Error parsing Nix evaluation result: {result}", file=sys.stderr)
return []
filtered_maintainers = [m for m in maintainers if m != pr_author]
if not filtered_maintainers:
print("No maintainers found for changed files (or only the PR author is a maintainer).", file=sys.stderr)
return []
else:
print(f"Found maintainers to notify: {' '.join(filtered_maintainers)}", file=sys.stderr)
return filtered_maintainers
def main() -> None:
"""Main function to handle command line arguments and run the extraction."""
parser = argparse.ArgumentParser(
description="Extract maintainers from changed plugin files"
)
parser.add_argument(
"--changed-files",
help="Newline-separated list of changed files",
default="",
)
parser.add_argument(
"--pr-author",
required=True,
help="GitHub username of the PR author",
)
args = parser.parse_args()
changed_files = [f.strip() for f in args.changed_files.split('\n') if f.strip()]
maintainers = extract_maintainers(changed_files, args.pr_author)
print(' '.join(maintainers))
if __name__ == "__main__":
main()