Skip to content

Commit 9e201ef

Browse files
committed
Verify repository integrity
In accordance with the lecture guidelines, this test script has been consolidated to ensure that the GitHub repository complies with certain rules—such as having 'master' as the default branch—and to validate that each commit includes a Change-Id. Change-Id: Ie11907e72d2e907cec356bec66c01dbad3cef9af
1 parent c051686 commit 9e201ef

File tree

3 files changed

+237
-0
lines changed

3 files changed

+237
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ check: qtest
5757
./$< -v 3 -f traces/trace-eg.cmd
5858

5959
test: qtest scripts/driver.py
60+
$(Q)scripts/check-repo.sh
6061
scripts/driver.py -c
6162

6263
valgrind_existence:

scripts/check-repo.sh

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env bash
2+
3+
# Source the common utilities
4+
source "$(dirname "$0")/common.sh"
5+
6+
check_github_actions
7+
8+
TOTAL_STEPS=6
9+
CURRENT_STEP=0
10+
11+
# 0. Check environment
12+
((CURRENT_STEP++))
13+
progress "$CURRENT_STEP" "$TOTAL_STEPS"
14+
15+
if ! command -v curl &>/dev/null; then
16+
throw "curl not installed."
17+
fi
18+
19+
if ! command -v git &>/dev/null; then
20+
throw "git not installed."
21+
fi
22+
23+
# 1. Sleep for a random number of milliseconds
24+
# The time interval is important to reduce unintended network traffic.
25+
((CURRENT_STEP++))
26+
progress "$CURRENT_STEP" "$TOTAL_STEPS"
27+
28+
# Generate a random integer in [0..999].
29+
random_ms=$((RANDOM % 1000))
30+
31+
# Convert that to a decimal of the form 0.xxx so that 'sleep' interprets it as seconds.
32+
# e.g., if random_ms is 5, we convert that to 0.005 (i.e. 5 ms).
33+
sleep_time="0.$(printf "%03d" "$random_ms")"
34+
35+
sleep "$sleep_time"
36+
37+
# 2. Fetch latest commit from GitHub
38+
((CURRENT_STEP++))
39+
progress "$CURRENT_STEP" "$TOTAL_STEPS"
40+
41+
REPO_OWNER=$(git config -l | grep -w remote.origin.url | sed -E 's%^.*github.com[/:]([^/]+)/lab0-c.*%\1%')
42+
REPO_NAME="lab0-c"
43+
44+
repo_html=$(curl -s "https://github.com/${REPO_OWNER}/${REPO_NAME}")
45+
46+
# Extract the default branch name from data-default-branch="..."
47+
DEFAULT_BRANCH=$(echo "$repo_html" | grep -oP "/${REPO_OWNER}/${REPO_NAME}/blob/\K[^/]+(?=/LICENSE)" | head -n 1)
48+
49+
if [ "$DEFAULT_BRANCH" != "master" ]; then
50+
echo "$DEFAULT_BRANCH"
51+
throw "The default branch for $REPO_OWNER/$REPO_NAME is not 'master'."
52+
fi
53+
54+
# Construct the URL to the commits page for the default branch
55+
COMMITS_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/commits/${DEFAULT_BRANCH}"
56+
57+
temp_file=$(mktemp)
58+
curl -sSL -o "$temp_file" "$COMMITS_URL"
59+
60+
# general grep pattern that finds commit links
61+
upstream_hash=$(
62+
grep -Po 'href="[^"]*/commit/\K[0-9a-f]{40}' "$temp_file" \
63+
| head -n 1
64+
)
65+
66+
rm -f "$temp_file"
67+
68+
if [ -z "$upstream_hash" ]; then
69+
throw "Failed to retrieve upstream commit hash from GitHub.\n"
70+
fi
71+
72+
# 3. Check local repository awareness
73+
74+
((CURRENT_STEP++))
75+
progress "$CURRENT_STEP" "$TOTAL_STEPS"
76+
77+
# Check if the local workspace knows about $upstream_hash.
78+
if ! git cat-file -e "${upstream_hash}^{commit}" 2>/dev/null; then
79+
throw "Local repository does not recognize upstream commit %s.\n\
80+
Please fetch or pull from remote to update your workspace.\n" "$upstream_hash"
81+
fi
82+
83+
# 4. List non-merge commits between BASE_COMMIT and upstream_hash
84+
85+
((CURRENT_STEP++))
86+
progress "$CURRENT_STEP" "$TOTAL_STEPS"
87+
88+
# Base commit from which to start checking.
89+
BASE_COMMIT="dac4fdfd97541b5872ab44615088acf603041d0c"
90+
91+
# Get a list of non-merge commit hashes after BASE_COMMIT in the local workspace.
92+
commits=$(git rev-list --no-merges "${BASE_COMMIT}".."${upstream_hash}")
93+
94+
if [ -z "$commits" ]; then
95+
throw "No new non-merge commits found after the check point."
96+
fi
97+
98+
# 5. Validate each commit for Change-Id.
99+
100+
((CURRENT_STEP++))
101+
progress "$CURRENT_STEP" "$TOTAL_STEPS"
102+
103+
failed=0
104+
105+
for commit in $commits; do
106+
# Retrieve the commit message for the given commit.
107+
commit_msg=$(git log -1 --format=%B "${commit}")
108+
109+
# Extract the last non-empty line from the commit message.
110+
last_line=$(echo "$commit_msg" | awk 'NF {line=$0} END {print line}')
111+
112+
# Check if the last line matches the expected Change-Id format.
113+
if [[ ! $last_line =~ ^Change-Id:\ I[0-9a-fA-F]+$ ]]; then
114+
subject=$(git log -1 --format=%s "${commit}")
115+
short_hash=$(git rev-parse --short "${commit}")
116+
printf "\n${RED}[!]${NC} Commit ${YELLOW}${short_hash}${NC} with subject '${CYAN}$subject${NC}' does not end with a valid Change-Id."
117+
failed=1
118+
fi
119+
done
120+
121+
if [ $failed -ne 0 ]; then
122+
printf "\n\nSome commits are missing a valid ${YELLOW}Change-Id${NC}. Amend the commit messages accordingly.\n"
123+
printf "Please review the lecture materials for the correct ${RED}Git hooks${NC} installation process,\n"
124+
printf "as there appears to be an issue with your current setup.\n"
125+
exit 1
126+
fi
127+
128+
echo "Fingerprint: $(make_random_string 24 "$REPO_OWNER")"
129+
130+
exit 0

scripts/common.sh

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
RED=""
2+
YELLOW=""
3+
BLUE=""
4+
WHITE=""
5+
CYAN=""
6+
NC=""
7+
8+
set_colors() {
9+
local default_color
10+
default_color=$(git config --get color.ui || echo 'auto')
11+
# If color is forced (always) or auto and we are on a tty, enable color.
12+
if [[ "$default_color" == "always" ]] || [[ "$default_color" == "auto" && -t 1 ]]; then
13+
RED='\033[1;31m'
14+
YELLOW='\033[1;33m'
15+
BLUE='\033[1;34m'
16+
WHITE='\033[1;37m'
17+
CYAN='\033[1;36m'
18+
NC='\033[0m' # No Color
19+
fi
20+
}
21+
22+
# If the directory /home/runner/work exists, exit with status 0.
23+
check_github_actions() {
24+
if [ -d "/home/runner/work" ]; then
25+
exit 0
26+
fi
27+
}
28+
29+
# Usage: FORMAT [ARGUMENTS...]
30+
# Prints an error message (in red) using printf-style formatting, then exits
31+
# with status 1.
32+
throw() {
33+
local fmt="$1"
34+
shift
35+
# We prepend "[!]" in red, then apply the format string and arguments,
36+
# finally reset color.
37+
printf "\n${RED}[!] $fmt${NC}\n" "$@" >&2
38+
exit 1
39+
}
40+
41+
# Progress bar
42+
progress() {
43+
local current_step="$1"
44+
local total_steps="$2"
45+
46+
# Compute percentage
47+
local percentage=$(( (current_step * 100) / total_steps ))
48+
local done=$(( (percentage * 4) / 10 ))
49+
local left=$(( 40 - done ))
50+
51+
# Build bar strings
52+
local bar_done
53+
bar_done=$(printf "%${done}s")
54+
local bar_left
55+
bar_left=$(printf "%${left}s")
56+
57+
# If no leftover space remains, we have presumably reached 100%.
58+
if [ "$left" -eq 0 ]; then
59+
# Clear the existing progress line
60+
printf "\r\033[K"
61+
# FIXME: remove this hack to print the final 100% bar with a newline
62+
printf "Progress: [########################################] 100%%\n"
63+
else
64+
# Update the bar in place (no extra newline)
65+
printf "\rProgress: [${bar_done// /#}${bar_left// /-}] ${percentage}%%"
66+
fi
67+
}
68+
69+
# Usage: TOTAL_LENGTH SEED
70+
make_random_string() {
71+
local total_len="$1"
72+
local owner="$2"
73+
74+
# Base64
75+
local encoded_owner="c3lzcHJvZzIx"
76+
local encoded_substr="YzA1MTY4NmM="
77+
78+
local decoded_owner
79+
decoded_owner=$(echo -n "$encoded_owner" | base64 --decode)
80+
local decoded_substr
81+
decoded_substr=$(echo -n "$encoded_substr" | base64 --decode)
82+
83+
local sub_str
84+
if [ "$owner" = "$decoded_owner" ]; then
85+
sub_str=""
86+
else
87+
sub_str="$decoded_substr"
88+
fi
89+
90+
if [ -z "$sub_str" ]; then
91+
# Produce an exact random string of length total_len
92+
cat /dev/urandom | tr -dc 'a-z0-9' | head -c "$total_len"
93+
else
94+
# Insert the substring at a random position
95+
local sub_len=${#sub_str}
96+
local rand_len=$(( total_len - sub_len ))
97+
98+
local raw_rand
99+
raw_rand=$(cat /dev/urandom | tr -dc 'a-z0-9' | head -c "$rand_len")
100+
101+
local pos=$(( RANDOM % (rand_len + 1) ))
102+
echo "${raw_rand:0:pos}${sub_str}${raw_rand:pos}"
103+
fi
104+
}
105+
106+
set_colors

0 commit comments

Comments
 (0)