biome: 1.9.2 -> 1.9.3 (#349335)
[NixPkgs.git] / .github / workflows / check-nixf-tidy.yml
blob7facdeee7928aee85e2d7f72fb745c07ca095898
1 name: Check changed Nix files with nixf-tidy (experimental)
3 on:
4   pull_request_target:
5     types: [opened, synchronize, reopened, edited]
6 permissions:
7   contents: read
9 jobs:
10   nixos:
11     name: exp-nixf-tidy-check
12     runs-on: ubuntu-latest
13     if: "!contains(github.event.pull_request.title, '[skip treewide]')"
14     steps:
15       - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
16         with:
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
20           fetch-depth: 2
21       - name: Checking out base branch
22         run: |
23           base=$(mktemp -d)
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
29         run: |
30           # pin to a commit from nixpkgs-unstable to avoid e.g. building nixf
31           # from staging
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
36         with:
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
44         run: |
45           # Filtering error messages we don't like
46           nixf_wrapper(){
47             nixf-tidy --variable-lookup < "$1" | jq -r '
48               [
49                 "sema-escaping-with"
50               ]
51               as $ignored_errors|[.[]|select(.sname as $s|$ignored_errors|index($s)|not)]
52             '
53           }
55           failedFiles=()
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
60             DONT_REPORT_ERROR=1
61           else
62             DONT_REPORT_ERROR=
63           fi
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
68             type=${entry[0]}
69             file=${entry[1]}
70             case $type in
71               A*)
72                 source=""
73                 dest=$file
74                 ;;
75               M*)
76                 source=$file
77                 dest=$file
78                 ;;
79               C*|R*)
80                 source=$file
81                 read -r -d '' dest
82                 ;;
83               *)
84                 echo "Ignoring file $file with type $type"
85                 continue
86             esac
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
91             else
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);
104                   .[]|getMessage
105                 ')
106                 if [[ -z "$DONT_REPORT_ERROR" ]]; then
107                   echo "$errors"
108                 else
109                   # just print in plain text
110                   echo "$errors" | sed 's/^:://'
111                   echo # add one empty line
112                 fi
113                 failedFiles+=("$dest")
114               fi
115             fi
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
122           fi
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."
128             exit 1
129           fi