1 name: Check changed Nix files with nixf-tidy (experimental)
5 types: [opened, synchronize, reopened, edited]
11 name: exp-nixf-tidy-check
12 runs-on: ubuntu-latest
13 if: "!contains(github.event.pull_request.title, '[skip treewide]')"
15 - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
17 # pull_request_target checks out the base branch by default
18 ref: refs/pull/${{ github.event.pull_request.number }}/merge
19 # Fetches the merge commit and its parents
21 - name: Checking out base branch
24 baseRev=$(git rev-parse HEAD^1)
25 git worktree add "$base" "$baseRev"
26 echo "baseRev=$baseRev" >> "$GITHUB_ENV"
27 echo "base=$base" >> "$GITHUB_ENV"
28 - name: Get Nixpkgs revision for nixf
30 # pin to a commit from nixpkgs-unstable to avoid e.g. building nixf
32 # This should not be a URL, because it would allow PRs to run arbitrary code in CI!
33 rev=$(jq -r .rev ci/pinned-nixpkgs.json)
34 echo "url=https://github.com/NixOS/nixpkgs/archive/$rev.tar.gz" >> "$GITHUB_ENV"
35 - uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
37 # explicitly enable sandbox
38 extra_nix_config: sandbox = true
39 nix_path: nixpkgs=${{ env.url }}
40 - name: Install nixf and jq
41 # provided jq is incompatible with our expression
42 run: "nix-env -f '<nixpkgs>' -iAP nixf jq"
43 - name: Check that Nix files pass nixf-tidy
45 # Filtering error messages we don't like
47 nixf-tidy --variable-lookup < "$1" | jq -r '
51 as $ignored_errors|[.[]|select(.sname as $s|$ignored_errors|index($s)|not)]
57 # Don't report errors to file overview
58 # to avoid duplicates when editing title and description
59 if [[ "${{ github.event.action }}" == 'edited' ]] && [[ -z "${{ github.event.edited.changes.base }}" ]]; then
64 # TODO: Make this more parallel
66 # Loop through all Nix files touched by the PR
67 while readarray -d '' -n 2 entry && (( ${#entry[@]} != 0 )); do
84 echo "Ignoring file $file with type $type"
88 if [[ -n "$source" ]] && [[ "$(nixf_wrapper ${{ env.base }}/"$source")" != '[]' ]] 2>/dev/null; then
89 echo "Ignoring file $file because it doesn't pass nixf-tidy in the base commit"
90 echo # insert blank line
92 nixf_report="$(nixf_wrapper "$dest")"
93 if [[ "$nixf_report" != '[]' ]]; then
94 echo "$dest doesn't pass nixf-tidy. Reported by nixf-tidy:"
95 errors=$(echo "$nixf_report" | jq -r --arg dest "$dest" '
96 def getLCur: "line=" + (.line+1|tostring) + ",col=" + (.column|tostring);
97 def getRCur: "endLine=" + (.line+1|tostring) + ",endColumn=" + (.column|tostring);
98 def getRange: "file=\($dest)," + (.lCur|getLCur) + "," + (.rCur|getRCur);
99 def getBody: . as $top|(.range|getRange) + ",title="+ .sname + "::" +
100 (.message|sub("{}" ; ($top.args.[]|tostring)));
101 def getNote: "\n::notice " + (.|getBody);
102 def getMessage: "::error " + (.|getBody) + (if (.notes|length)>0 then
103 ([.notes.[]|getNote]|add) else "" end);
106 if [[ -z "$DONT_REPORT_ERROR" ]]; then
109 # just print in plain text
110 echo "$errors" | sed 's/^:://'
111 echo # add one empty line
113 failedFiles+=("$dest")
116 done < <(git diff -z --name-status ${{ env.baseRev }} -- '*.nix')
118 if [[ -n "$DONT_REPORT_ERROR" ]]; then
119 echo "Edited the PR but didn't change the base branch, only the description/title."
120 echo "Not reporting errors again to avoid duplication."
121 echo # add one empty line
124 if (( "${#failedFiles[@]}" > 0 )); then
125 echo "Some new/changed Nix files don't pass nixf-tidy."
126 echo "See ${{ github.event.pull_request.html_url }}/files for reported errors."
127 echo "If you believe this is a false positive, ping @Aleksanaa and @inclyc in this PR."