name: CODEOWNERS checks # Any change in triggers needs to be reflected in the concurrency group. on: pull_request: branches: - master - ft/master/** permissions: read-all concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: check_changes: name: Deduce required tests from code changes runs-on: ubuntu-latest outputs: added-files: ${{ steps.changes.outputs.added-files }} deleted-files: ${{ steps.changes.outputs.deleted-files }} codeowners-changed: ${{ steps.changes.outputs.codeowners-changed }} steps: - name: Check code changes uses: dorny/paths-filter@b2feaf19c27470162a626bd6fa8438ae5b263721 id: changes with: filters: | added-files: - added: '**' deleted-files: - deleted: '**' codeowners-changed: - 'CODEOWNERS' - '.github/workflows/lint-codeowners.yaml' codeowners: needs: check_changes if: ${{ needs.check_changes.outputs.codeowners-changed == 'true' || needs.check_changes.outputs.added-files == 'true' || needs.check_changes.outputs.deleted-files == 'true' }} name: Check CODEOWNERS consistency runs-on: ubuntu-latest steps: - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 with: persist-credentials: false # Hard-code the path instead of using ${{ github.repository }} # to make sure it works for forked repo as well. path: src/github.com/cilium/cilium - name: Check if all files have attributed code owners if: ${{ needs.check_changes.outputs.codeowners-changed == 'true' || needs.check_changes.outputs.added-files == 'true' }} run: | # CODEOWNERS patterns follows nearly the same syntax as a .gitignore. # To check if all files are covered by patterns other than the # catch-all '*', we turn the file into a .gitignore and list # unmatched files. cd src/github.com/cilium/cilium # Copy all patterns from CODEOWNERS, but skipping the comments # ('^[^#]') and the catch-all '*' rule (the only one with a single # character, we skip it with '^.[^ ]'). awk '/^[^#][^ ]/ {print $1}' CODEOWNERS > .gitignore # Reinitialize the repo and list all files NOT covered by .gitignore. rm -rf .git git init -q if [[ -n "$(git ls-files --others -X .gitignore)" ]]; then echo '::error title=missing_code_owners::Following files have no owners in CODEOWNERS:' git ls-files --others -X .gitignore exit 1 fi - name: Check if CODEOWNERS has stale entries if: ${{ needs.check_changes.outputs.codeowners-changed == 'true' || needs.check_changes.outputs.deleted-files == 'true' }} run: | cd src/github.com/cilium/cilium EXIT_STATUS=0 # We go through the patterns in CODEOWNERS, and for each of them we # search for corresponding files in the repo. while read l; do case "${l}" in /*) # The pattern should match from the root of the repo, # we'll use 'ls'. For now, just append pattern to $LIST. LIST+=" ${l#/}" ;; *) # No leading slash: may not be at the root of the repo, # search with 'find'. Print pattern if no file found. if [[ -z $(find . -path "*${l}*" -print -quit) ]]; then echo "${l}" EXIT_STATUS=1 fi ;; esac done <<< $(awk '/^[^#][^ ]/ {print $1}' CODEOWNERS) # Just one final call to 'ls' with all /* patterns found. Catch # patterns with no corresponding files/directories from stderr. STALE_PATTERNS="$(ls -- ${LIST} 2>&1 >/dev/null | sed "s|.*'\(.*\)':.*|/\1|")" if [[ -n "${STALE_PATTERNS}" ]]; then echo "${STALE_PATTERNS}" | sed 's/ /\n/g' EXIT_STATUS=1 fi if [[ ${EXIT_STATUS} -ne 0 ]]; then echo '::error title=stale_patterns::The patterns above should be removed from CODEOWNERS.' exit ${EXIT_STATUS} fi - name: Check if all teams in CODEOWNERS rules are documented in the file if: ${{ needs.check_changes.outputs.codeowners-changed == 'true' }} run: | EXIT_STATUS=0 # List all teams used in CODEOWNERS rules: discard comments and empty # lines, discard lines with no team assigned (with no space in it), # then discard the first field (pattern to match) for the remaining # rules, split the list of teams by replacing spaces with line # breaks, sort the results. Then grep for each team name among # CODEOWNERS's comments. cd src/github.com/cilium/cilium for team in $(sed -e '/^\(#\|$\)/d' -e '/^[^ ]*$/d' -e 's/^[^ #]\+ //' -e 's/ /\n/g' CODEOWNERS | sort -u); do if ! grep -q "^# ${team} " CODEOWNERS; then echo "${team}"; EXIT_STATUS=1 fi; done if [[ ${EXIT_STATUS} -ne 0 ]]; then echo '::error title=missing_team::The teams above are not documented in CODEOWNERS. Typo?' exit ${EXIT_STATUS} fi