Skip to content

Commit 8f732b5

Browse files
committed
Add additional tests + use script
1 parent bdd4d58 commit 8f732b5

File tree

3 files changed

+143
-112
lines changed

3 files changed

+143
-112
lines changed
Lines changed: 25 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,25 @@
1-
name: CODEOWNERS Validation
2-
run-name: ${{ github.actor }} is validating CODEOWNERS
3-
on:
4-
pull_request:
5-
branches:
6-
- main
7-
8-
jobs:
9-
CheckDockerfileCodeowners:
10-
runs-on: ubuntu-latest
11-
steps:
12-
- name: Check out repository code
13-
uses: actions/checkout@v4
14-
15-
- name: Define functions
16-
run: |
17-
#!/bin/bash
18-
set -e
19-
20-
read_codeowners_file() {
21-
local codeOwnersFilePath="CODEOWNERS"
22-
local codeOwnerPaths=()
23-
24-
while IFS= read -r line; do
25-
if [[ "$line" =~ ^\s*# ]] || [[ -z "$line" ]] || [[ "$line" =~ ^[[:space:]]*$ ]]; then
26-
continue
27-
fi
28-
29-
path=$(echo "$line" | cut -d' ' -f1 | sed 's/^[[:space:]]*//' | sed 's/[\/&]/\\&/g' | sed 's/\./\\./g' | sed 's/\*/.*/g')
30-
codeOwnerPaths+=("$path")
31-
done < "$codeOwnersFilePath"
32-
33-
echo "${codeOwnerPaths[@]}"
34-
}
35-
36-
export -f read_codeowners_file
37-
38-
- name: Ensure each CODEOWNER is a team
39-
run: |
40-
#!/bin/bash
41-
set -e
42-
43-
codeOwnersFilePath="CODEOWNERS"
44-
codeOwners=$(grep -v '^#' "$codeOwnersFilePath" | cut -d' ' -f2)
45-
46-
for codeOwner in $codeOwners; do
47-
if [[ "$codeOwner" != *"/"* ]]; then
48-
echo "The CODEOWNERS file must contain teams, not individual users."
49-
exit 1
50-
fi
51-
done
52-
53-
- name: Check each Dockerfile for a CODEOWNER
54-
run: |
55-
#!/bin/bash
56-
set -e
57-
58-
codeOwnerPaths=($(read_codeowners_file))
59-
60-
filesWithoutOwner=()
61-
dockerfiles=$(find . -type f -name "Dockerfile")
62-
63-
for dockerfile in $dockerfiles; do
64-
ownerFound=false
65-
for path in "${codeOwnerPaths[@]}"; do
66-
if [[ "$dockerfile" =~ $path ]]; then
67-
ownerFound=true
68-
break
69-
fi
70-
done
71-
72-
if [[ "$ownerFound" == false ]]; then
73-
filesWithoutOwner+=("$dockerfile")
74-
fi
75-
done
76-
77-
if [[ ${#filesWithoutOwner[@]} -gt 0 ]]; then
78-
echo "The following Dockerfiles do not have an owner in the CODEOWNERS file:"
79-
printf "%s\n" "${filesWithoutOwner[@]}"
80-
exit 1
81-
fi
82-
83-
- name: Check for unused CODEOWNER paths
84-
run: |
85-
#!/bin/bash
86-
set -e
87-
88-
codeOwnerPaths=($(read_codeowners_file))
89-
90-
unusedPaths=()
91-
allFiles=$(find . -type f)
92-
93-
for path in "${codeOwnerPaths[@]}"; do
94-
pathUsed=false
95-
for file in $allFiles; do
96-
if [[ "$file" =~ $path ]]; then
97-
pathUsed=true
98-
break
99-
fi
100-
done
101-
102-
if [[ "$pathUsed" == false ]]; then
103-
unusedPaths+=("$path")
104-
fi
105-
done
106-
107-
if [[ ${#unusedPaths[@]} -gt 0 ]]; then
108-
echo "The following paths in the CODEOWNERS file are not used by any file in the repository:"
109-
printf "%s\n" "${unusedPaths[@]}"
110-
exit 1
111-
fi
1+
name: CODEOWNERS Validation
2+
run-name: ${{ github.actor }} is validating CODEOWNERS
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
8+
jobs:
9+
CheckDockerfileCodeowners:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Check out repository code
13+
uses: actions/checkout@v4
14+
15+
- name: Ensure each CODEOWNER is a team
16+
if: always()
17+
run: ./validate-codeowners.sh ownersAreTeams
18+
19+
- name: Check each Dockerfile for a CODEOWNER
20+
if: always()
21+
run: ./validate-codeowners.sh dockerfilesHaveOwners
22+
23+
- name: Check for unused CODEOWNER paths
24+
if: always()
25+
run: ./validate-codeowners.sh pathsAreUsed

CODEOWNERS

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,3 @@ src/ubuntu/common/coredeps/ @dotnet/runtime-infrastructure
5050
src/ubuntu/20.04/Dockerfile @dotnet/source-build-internal
5151
src/ubuntu/22.04/Dockerfile @dotnet/source-build-internal
5252
src/ubuntu/24.04/Dockerfile @dotnet/source-build-internal
53-

validate-codeowners.sh

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
if [ $# -eq 0 ]; then
6+
echo "No function name provided. Usage: ./test.sh <ownersAreTeams|pathsAreUsed|dockerfilesHaveOwners>"
7+
exit 1
8+
fi
9+
10+
declare -A codeOwnerEntries
11+
readCodeOwnersFile() {
12+
codeOwnersFilePath="CODEOWNERS"
13+
14+
while IFS= read -r line; do
15+
# Skip blank lines and comments
16+
if [[ "$line" =~ ^\s*# ]] || [[ -z "$line" ]] || [[ "$line" =~ ^[[:space:]]*$ ]]; then
17+
continue
18+
fi
19+
20+
path=$(echo "$line" | awk '{print $1}' | sed 's/[[:space:]]*$//')
21+
owner=$(echo "$line" | sed 's/^[^ ]* //' | sed 's/[[:space:]]*$//')
22+
23+
# Escape periods
24+
path=$(echo "$path" | sed 's/\./\\./g')
25+
26+
# Single * matches anything that is not a slash
27+
# Double ** matches anything
28+
# Trailing / matches anything
29+
# Remove leading slashes
30+
path=$(echo "$path" | sed -E 's/([^*]|^)\*([^*]|$)/\1[^\/]*\2/g')
31+
path=$(echo "$path" | sed 's/\*\*/.*/g')
32+
if [[ "${path: -1}" == "/" ]]; then
33+
path="$path.*"
34+
fi
35+
path=$(echo "$path" | sed 's/^\///')
36+
path="^$path$"
37+
38+
# Use git check-ignore to determine if the path matches the patterns
39+
codeOwnerEntries["$path"]="$owner"
40+
done < "$codeOwnersFilePath"
41+
}
42+
43+
ownersAreTeams() {
44+
nonTeamOwners=()
45+
46+
for codeOwner in "${codeOwnerEntries[@]}"; do
47+
if [[ "$codeOwner" != *"/"* ]]; then
48+
nonTeamOwners+=("$codeOwner")
49+
fi
50+
done
51+
52+
if [[ ${#nonTeamOwners[@]} -gt 0 ]]; then
53+
echo "The following CODEOWNERS are not teams:"
54+
printf "%s\n" "${nonTeamOwners[@]}"
55+
exit 1
56+
fi
57+
58+
exit 0
59+
}
60+
61+
pathsAreUsed() {
62+
allFiles=$(find . -type f | sed 's/^\.\///')
63+
unusedPaths=()
64+
65+
for path in "${!codeOwnerEntries[@]}"; do
66+
pathUsed=false
67+
for file in $allFiles; do
68+
if [[ "$file" =~ $path ]]; then
69+
pathUsed=true
70+
break
71+
fi
72+
done
73+
74+
if [[ "$pathUsed" == false ]]; then
75+
# Undo regex changes
76+
path=$(echo "$path" | sed 's/\[\^\/\]\*/\*/g' | sed 's/^\^//' | sed 's/\$$//')
77+
unusedPaths+=("$path")
78+
fi
79+
done
80+
81+
if [[ ${#unusedPaths[@]} -gt 0 ]]; then
82+
echo "The following paths in the CODEOWNERS file are not used by any file in the repository:"
83+
printf "%s\n" "${unusedPaths[@]}"
84+
exit 1
85+
fi
86+
87+
exit 0
88+
}
89+
90+
dockerfilesHaveOwners() {
91+
dockerfiles=$(find . -type f -name "Dockerfile" | sed 's/^\.\///')
92+
filesWithoutOwner=()
93+
94+
for file in $dockerfiles; do
95+
ownerFound=false
96+
for pattern in "${!codeOwnerEntries[@]}"; do
97+
if [[ "$file" =~ $pattern ]]; then
98+
ownerFound=true
99+
break
100+
fi
101+
done
102+
if [ "$ownerFound" = false ]; then
103+
filesWithoutOwner+=("$file")
104+
fi
105+
done
106+
107+
if [[ ${#filesWithoutOwner[@]} -gt 0 ]]; then
108+
echo "The following Dockerfiles do not have an owner in the CODEOWNERS file:"
109+
printf "%s\n" "${filesWithoutOwner[@]}"
110+
exit 1
111+
fi
112+
113+
exit 0
114+
}
115+
116+
# Call the function passed as an argument
117+
readCodeOwnersFile
118+
"$1"

0 commit comments

Comments
 (0)