mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-09-28 21:43:42 +08:00
Sync: commaai/openpilot:master -> sunnypilot/sunnypilot:master
# Conflicts: # .gitmodules # .lfsconfig # opendbc_repo # openpilot/selfdrive/modeld/SConscript # openpilot/selfdrive/modeld/modeld.py # openpilot/selfdrive/ui/mici/layouts/settings/toggles.py # openpilot/system/sentry.py # panda # scripts/lint/lint.sh # tinygrad_repo # tools/release/build_stripped.sh # tools/release/check-submodules.sh # uv.lock
This commit is contained in:
+2
-2
@@ -6,6 +6,6 @@ if [ $# -eq 0 ]; then
|
||||
fi
|
||||
|
||||
BASE="https://github.com/commaai/openpilot/pull/"
|
||||
PR_NUM="$(echo $1 | grep -o -E '[0-9]+')"
|
||||
PR_NUM="$(echo "$1" | grep -o -E '[0-9]+')"
|
||||
|
||||
curl -L $BASE/$PR_NUM.patch | git apply -3
|
||||
curl -L "$BASE/$PR_NUM.patch" | git apply -3
|
||||
|
||||
@@ -7,10 +7,10 @@ if [ $# -eq 0 ]; then
|
||||
fi
|
||||
|
||||
BASE="https://github.com/commaai/openpilot/pull/"
|
||||
PR_NUM="$(echo $1 | grep -o -E '[0-9]+')"
|
||||
PR_NUM="$(echo "$1" | grep -o -E '[0-9]+')"
|
||||
BRANCH=tmp-pr${PR_NUM}
|
||||
|
||||
git branch -D -f $BRANCH || true
|
||||
git fetch -u -f origin pull/$PR_NUM/head:$BRANCH
|
||||
git switch $BRANCH
|
||||
git branch -D -f "$BRANCH" || true
|
||||
git fetch -u -f origin "pull/$PR_NUM/head:$BRANCH"
|
||||
git switch "$BRANCH"
|
||||
git reset --hard FETCH_HEAD
|
||||
|
||||
@@ -11,7 +11,7 @@ BRANCH="master"
|
||||
RUNS="20"
|
||||
|
||||
COOKIE_JAR=/tmp/cookies
|
||||
CRUMB=$(curl -s --cookie-jar $COOKIE_JAR 'https://jenkins.comma.life/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')
|
||||
CRUMB=$(curl -s --cookie-jar "$COOKIE_JAR" 'https://jenkins.comma.life/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')
|
||||
|
||||
FIRST_LOOP=1
|
||||
|
||||
@@ -25,13 +25,13 @@ function loop() {
|
||||
|
||||
if [[ $FIRST_LOOP ]]; then
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
GIT_LFS_SKIP_SMUDGE=1 git clone --quiet -b $BRANCH --depth=1 --no-tags git@github.com:commaai/openpilot $TEMP_DIR
|
||||
git -C $TEMP_DIR checkout --quiet -b $JENKINS_BRANCH
|
||||
echo "TESTING: $(date)" >> $TEMP_DIR/testing_jenkins
|
||||
git -C $TEMP_DIR add testing_jenkins
|
||||
git -C $TEMP_DIR commit --quiet -m "testing"
|
||||
git -C $TEMP_DIR push --quiet -f origin $JENKINS_BRANCH
|
||||
rm -rf $TEMP_DIR
|
||||
GIT_LFS_SKIP_SMUDGE=1 git clone --quiet -b "$BRANCH" --depth=1 --no-tags git@github.com:commaai/openpilot "$TEMP_DIR"
|
||||
git -C "$TEMP_DIR" checkout --quiet -b "$JENKINS_BRANCH"
|
||||
echo "TESTING: $(date)" >> "$TEMP_DIR/testing_jenkins"
|
||||
git -C "$TEMP_DIR" add testing_jenkins
|
||||
git -C "$TEMP_DIR" commit --quiet -m "testing"
|
||||
git -C "$TEMP_DIR" push --quiet -f origin "$JENKINS_BRANCH"
|
||||
rm -rf "$TEMP_DIR"
|
||||
FIRST_BUILD=1
|
||||
echo ''
|
||||
echo 'waiting on Jenkins...'
|
||||
@@ -40,15 +40,15 @@ function loop() {
|
||||
FIRST_LOOP=""
|
||||
fi
|
||||
|
||||
FIRST_BUILD=$(curl -s $API_ROUTE/api/json | jq .nextBuildNumber)
|
||||
FIRST_BUILD=$(curl -s "$API_ROUTE/api/json" | jq .nextBuildNumber)
|
||||
LAST_BUILD=$((FIRST_BUILD+N-1))
|
||||
TEST_BUILDS=( $(seq $FIRST_BUILD $LAST_BUILD) )
|
||||
read -r -a TEST_BUILDS <<< "$(seq -s ' ' "$FIRST_BUILD" "$LAST_BUILD")"
|
||||
|
||||
# Start N new builds
|
||||
for i in ${TEST_BUILDS[@]};
|
||||
for i in "${TEST_BUILDS[@]}";
|
||||
do
|
||||
echo "Starting build $i"
|
||||
curl -s --output /dev/null --cookie $COOKIE_JAR -H "$CRUMB" -X POST $API_ROUTE/build?delay=0sec
|
||||
curl -s --output /dev/null --cookie "$COOKIE_JAR" -H "$CRUMB" -X POST "$API_ROUTE/build?delay=0sec"
|
||||
sleep 5
|
||||
done
|
||||
echo ""
|
||||
@@ -58,14 +58,14 @@ function loop() {
|
||||
sleep 30
|
||||
|
||||
count=0
|
||||
for i in ${TEST_BUILDS[@]};
|
||||
for i in "${TEST_BUILDS[@]}";
|
||||
do
|
||||
RES=$(curl -s -w "\n%{http_code}" --cookie $COOKIE_JAR -H "$CRUMB" $API_ROUTE/$i/api/json)
|
||||
RES=$(curl -s -w "\n%{http_code}" --cookie "$COOKIE_JAR" -H "$CRUMB" "$API_ROUTE/$i/api/json")
|
||||
HTTP_CODE=$(tail -n1 <<< "$RES")
|
||||
JSON=$(sed '$ d' <<< "$RES")
|
||||
|
||||
if [[ $HTTP_CODE == "200" ]]; then
|
||||
STILL_RUNNING=$(echo $JSON | jq .inProgress)
|
||||
STILL_RUNNING=$(echo "$JSON" | jq .inProgress)
|
||||
if [[ $STILL_RUNNING == "true" ]]; then
|
||||
echo -e "Build $i: ${YELLOW}still running${NC}"
|
||||
continue
|
||||
@@ -119,11 +119,11 @@ function _looper() {
|
||||
echo -e "You are about to start $RUNS Jenkins builds against the $BRANCH branch."
|
||||
echo -e "If you expect this to run overnight, ${UNDERLINE}${BOLD}unplug the cold reboot power switch${NC} from the testing closet before."
|
||||
echo ""
|
||||
read -p "Press (y/Y) to confirm: " choice
|
||||
read -r -p "Press (y/Y) to confirm: " choice
|
||||
if [[ "$choice" == "y" || "$choice" == "Y" ]]; then
|
||||
loop
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
_looper $@
|
||||
_looper "$@"
|
||||
|
||||
@@ -4,4 +4,4 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
|
||||
|
||||
export FINGERPRINT="TOYOTA_COROLLA_TSS2"
|
||||
export SKIP_FW_QUERY="1"
|
||||
$DIR/../launch_openpilot.sh
|
||||
"$DIR/../launch_openpilot.sh"
|
||||
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import tomllib
|
||||
import importlib.metadata
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if sys.prefix == sys.base_prefix:
|
||||
print("Dependency checks require a virtual environment. Run tools/op.sh setup first.")
|
||||
return 1
|
||||
|
||||
project = tomllib.loads((Path(__file__).resolve().parents[2] / "pyproject.toml").read_text())["project"]
|
||||
direct = len(project["dependencies"]) + sum(len(deps) for deps in project["optional-dependencies"].values())
|
||||
# Count each installed package once, including transitive dependencies and all extras.
|
||||
packages = {dist.metadata["Name"].lower().replace("_", "-") for dist in importlib.metadata.distributions()}
|
||||
# Logical file sizes avoid filesystem block-size differences. Don't follow links to the interpreter or source tree.
|
||||
size = sum(path.stat().st_size for path in Path(sys.prefix).rglob("*") if not path.is_symlink() and path.is_file())
|
||||
|
||||
"""
|
||||
This test prevents our depency footprint from growing.
|
||||
These values are *not* intended to be increased, and we
|
||||
expect to strictly drive these down over time.
|
||||
"""
|
||||
failed = False
|
||||
for name, value, limit in (
|
||||
("Direct dependencies (all extras)", direct, 39),
|
||||
("Total dependencies", len(packages), 84),
|
||||
("Venv size (MiB)", size / 1024**2, 570),
|
||||
):
|
||||
print(f"{name}: {value:g} (limit: {limit})")
|
||||
failed |= value > limit
|
||||
return int(failed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
FAIL=0
|
||||
|
||||
if grep -n '\(#\|//\)\([[:space:]]*\)NOMERGE' $@; then
|
||||
if grep -n '\(#\|//\)\([[:space:]]*\)NOMERGE' "$@"; then
|
||||
echo -e "NOMERGE comments found! Remove them before merging\n"
|
||||
FAIL=1
|
||||
fi
|
||||
|
||||
exit $FAIL
|
||||
exit "$FAIL"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
FAIL=0
|
||||
|
||||
if grep '^#!.*python' $@ | grep -v '#!/usr/bin/env python3$'; then
|
||||
if grep -I '^#!.*python' "$@" | grep -v '#!/usr/bin/env python3$'; then
|
||||
echo -e "Invalid shebang! Must use '#!/usr/bin/env python3'\n"
|
||||
FAIL=1
|
||||
fi
|
||||
|
||||
if grep '^#!.*bash' $@ | grep -v '#!/usr/bin/env bash$'; then
|
||||
if grep -I '^#!.*bash' "$@" | grep -v '#!/usr/bin/env bash$'; then
|
||||
echo -e "Invalid shebang! Must use '#!/usr/bin/env bash'"
|
||||
FAIL=1
|
||||
fi
|
||||
|
||||
exit $FAIL
|
||||
exit "$FAIL"
|
||||
|
||||
Executable
+132
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
"""A minimal shellcheck-like static analysis tool for shell scripts.
|
||||
|
||||
Covers syntax, unquoted expansions, scalar $@ assignments, and read -r.
|
||||
"""
|
||||
import re
|
||||
import argparse
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
VARIABLE = re.compile(r"\$(?:\{[^}\n]*\}|[A-Za-z_]\w*|[@*0-9])")
|
||||
ASSIGNMENT = re.compile(r"[A-Za-z_]\w*(?:\[[^]]*\])?\+?=")
|
||||
OPAQUE = re.compile(r"\$?\(\([^\n]*?\)\)|\[\[.*?\]\]", re.DOTALL)
|
||||
HEREDOC = re.compile(r"<<(-?)\s*('[^']+'|\"[^\"]+\"|\\?[A-Za-z_]\w*)")
|
||||
|
||||
|
||||
def commands(text):
|
||||
def scan(i=0, end="", pattern_group=False):
|
||||
start, quote, words, expansions, documents = i, False, [], [], []
|
||||
cases = []
|
||||
while i < len(text):
|
||||
c = text[i]
|
||||
if c == "\\":
|
||||
i += 2
|
||||
continue
|
||||
if c == "'" and not quote:
|
||||
i = text.find("'", i + 1) + 1 or len(text)
|
||||
continue
|
||||
if c == '"':
|
||||
quote = not quote
|
||||
elif (not quote or text.startswith("$((", i)) and (match := OPAQUE.match(text, i)):
|
||||
i = match.end()
|
||||
continue
|
||||
elif text.startswith("$(", i):
|
||||
if not quote:
|
||||
expansions.append(i)
|
||||
i = yield from scan(i + 2, ")")
|
||||
continue
|
||||
elif match := VARIABLE.match(text, i):
|
||||
if not quote and not match[0].startswith("${#"):
|
||||
expansions.append(i)
|
||||
i = match.end()
|
||||
continue
|
||||
elif not quote:
|
||||
if text.startswith("<<<", i):
|
||||
i += 3
|
||||
continue
|
||||
if c == "#" and i == start:
|
||||
i = text.find("\n", i)
|
||||
i = len(text) if i < 0 else i
|
||||
start = i
|
||||
continue
|
||||
if not text.startswith("<<<", i) and (match := HEREDOC.match(text, i)):
|
||||
documents.append((match[2].strip("'\"").lstrip("\\"), bool(match[1])))
|
||||
i = match.end()
|
||||
continue
|
||||
if c in " \t\r\n;|&()":
|
||||
if start < i:
|
||||
words.append((text[start:i], start, expansions))
|
||||
if len(words) >= 3 and words[0][0] == "case" and words[-1][0] == "in":
|
||||
cases.append(True)
|
||||
words = []
|
||||
if words and words[0][0] == "esac" and cases:
|
||||
cases.pop()
|
||||
words = []
|
||||
pattern = bool(cases and cases[-1])
|
||||
expansions = []
|
||||
if c in "\n;|&()":
|
||||
if words and not (pattern or pattern_group) and (c != ")" or end):
|
||||
yield words
|
||||
words = []
|
||||
if c == end and not pattern:
|
||||
return i + 1
|
||||
if c == ")" and pattern:
|
||||
cases[-1] = False
|
||||
if cases and (terminator := re.match(r";(?:;&|;|&)", text[i:])):
|
||||
cases[-1] = True
|
||||
i += len(terminator[0]) - 1
|
||||
if c == "(":
|
||||
if pattern and i == start:
|
||||
cases[-1] = False
|
||||
i = yield from scan(i + 1, ")", pattern or pattern_group)
|
||||
start = i
|
||||
continue
|
||||
if c == "\n":
|
||||
for delimiter, strip_tabs in documents:
|
||||
while i < len(text):
|
||||
stop = text.find("\n", i + 1)
|
||||
stop = len(text) if stop < 0 else stop
|
||||
line, i = text[i + 1:stop], stop
|
||||
if (line.lstrip("\t") if strip_tabs else line) == delimiter:
|
||||
break
|
||||
documents = []
|
||||
start = i + 1
|
||||
i += 1
|
||||
if start < i:
|
||||
words.append((text[start:i], start, expansions))
|
||||
if words:
|
||||
yield words
|
||||
return i
|
||||
yield from scan()
|
||||
|
||||
|
||||
def check_text(text):
|
||||
for words in commands(text):
|
||||
command = next((w for w, _, _ in words if not ASSIGNMENT.match(w) and w not in {"if", "then", "elif", "while", "until", "do", "!"}), "")
|
||||
prefix = True
|
||||
for index, (word, start, expansions) in enumerate(words):
|
||||
assignment = ASSIGNMENT.match(word) and (prefix or command in {"export", "local", "declare", "readonly", "typeset"})
|
||||
prefix = prefix and (bool(assignment) or word in {"if", "then", "elif", "while", "until", "do", "!"})
|
||||
for offset in expansions:
|
||||
array = word in {"$@", "$*"} or "[@]" in word or "[*]" in word
|
||||
if not assignment and (command not in {"case", "for", "select"} or array) and not (index and words[index - 1][0] == "<<<"):
|
||||
yield text.count("\n", 0, offset) + 1, "Quote this expansion to prevent word splitting and globbing"
|
||||
if assignment and re.fullmatch(r'"[^"\n]*\$@[^"\n]*"', ASSIGNMENT.sub("", word, count=1)):
|
||||
yield text.count("\n", 0, start) + 1, 'Use an array for "$@", or "$*" to join arguments'
|
||||
if command == "read" and not any(re.fullmatch(r"-[A-Za-z]*r[A-Za-z0-9]*", w) for w, _, _ in words):
|
||||
yield text.count("\n", 0, next(start for w, start, _ in words if w == "read")) + 1, "Use read -r to preserve backslashes"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("filenames", nargs="+")
|
||||
failed = False
|
||||
for filename in parser.parse_args().filenames:
|
||||
syntax = subprocess.run(["bash", "-n", "--", filename], check=False)
|
||||
failed |= syntax.returncode != 0
|
||||
if syntax.returncode == 0:
|
||||
for line, message in check_text(Path(filename).read_text()):
|
||||
print(f"{filename}:{line}: {message}")
|
||||
failed = True
|
||||
raise SystemExit(failed)
|
||||
+37
-24
@@ -9,7 +9,7 @@ NC='\033[0m'
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
ROOT="$DIR/../../"
|
||||
cd $ROOT
|
||||
cd "$ROOT"
|
||||
|
||||
FAILED=0
|
||||
|
||||
@@ -26,10 +26,10 @@ function run() {
|
||||
done
|
||||
|
||||
shift 1;
|
||||
CMD="$@"
|
||||
CMD=("$@")
|
||||
|
||||
set +e
|
||||
log="$((eval "$CMD" ) 2>&1)"
|
||||
log="$("${CMD[@]}" 2>&1)"
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo -e "[${GREEN}✔${NC}]"
|
||||
@@ -42,22 +42,21 @@ function run() {
|
||||
}
|
||||
|
||||
function run_tests() {
|
||||
ALL_FILES=$1
|
||||
PYTHON_FILES=$2
|
||||
|
||||
run "ruff" ruff check openpilot --quiet
|
||||
run "check_indentation" $DIR/check_indentation.py $PYTHON_FILES
|
||||
run "check_added_large_files" $DIR/check_added_large_files.py --maxkb=120 $ALL_FILES
|
||||
run "check_shebang_scripts_are_executable" $DIR/check_shebang_scripts_are_executable.py $ALL_FILES
|
||||
run "check_shebang_format" $DIR/check_shebang_format.sh $ALL_FILES
|
||||
run "check_nomerge_comments" $DIR/check_nomerge_comments.sh $ALL_FILES
|
||||
run "check_shell" python3 "$DIR/check_shell.py" "${SHELL_FILES[@]}"
|
||||
run "check_dependencies" python3 "$DIR/check_dependencies.py"
|
||||
run "check_indentation" "$DIR/check_indentation.py" "${PYTHON_FILES[@]}"
|
||||
run "check_added_large_files" "$DIR/check_added_large_files.py" --maxkb=120 "${ALL_FILES[@]}"
|
||||
run "check_shebang_scripts_are_executable" "$DIR/check_shebang_scripts_are_executable.py" "${ALL_FILES[@]}"
|
||||
run "check_shebang_format" "$DIR/check_shebang_format.sh" "${ALL_FILES[@]}"
|
||||
run "check_nomerge_comments" "$DIR/check_nomerge_comments.sh" "${ALL_FILES[@]}"
|
||||
|
||||
if [[ -z "$FAST" ]]; then
|
||||
run "ty" ty check openpilot
|
||||
run "codespell" codespell $ALL_FILES --ignore-words=$ROOT/.codespellignore
|
||||
run "codespell" codespell "${ALL_FILES[@]}" --ignore-words="$ROOT/.codespellignore"
|
||||
fi
|
||||
|
||||
return $FAILED
|
||||
return "$FAILED"
|
||||
}
|
||||
|
||||
function help() {
|
||||
@@ -67,6 +66,8 @@ function help() {
|
||||
echo ""
|
||||
echo -e "${BOLD}${UNDERLINE}Tests:${NC}"
|
||||
echo -e " ${BOLD}ruff${NC}"
|
||||
echo -e " ${BOLD}check_shell${NC}"
|
||||
echo -e " ${BOLD}check_dependencies${NC}"
|
||||
echo -e " ${BOLD}check_indentation${NC}"
|
||||
echo -e " ${BOLD}ty${NC}"
|
||||
echo -e " ${BOLD}codespell${NC}"
|
||||
@@ -101,17 +102,29 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
RUN=$([ -z "$RUN" ] && echo "" || echo "!($(echo $RUN | sed 's/ /|/g'))")
|
||||
SKIP="@($(echo $SKIP | sed 's/ /|/g'))"
|
||||
RUN=$([ -z "$RUN" ] && echo "" || echo "!($(echo "$RUN" | sed 's/ /|/g'))")
|
||||
SKIP="@($(echo "$SKIP" | sed 's/ /|/g'))"
|
||||
|
||||
IGNORED_DIRS="^openpilot/third_party/.*"
|
||||
GIT_FILES="$(git ls-files openpilot | grep -vE "$IGNORED_DIRS")"
|
||||
ALL_FILES=""
|
||||
for f in $GIT_FILES; do
|
||||
if [[ -f $f ]]; then
|
||||
ALL_FILES+="$f"$'\n'
|
||||
ALL_FILES=()
|
||||
PYTHON_FILES=()
|
||||
while IFS= read -r -d '' f; do
|
||||
if [[ $f =~ ^openpilot/third_party/.* ]]; then
|
||||
continue
|
||||
fi
|
||||
done
|
||||
PYTHON_FILES=$(echo "$ALL_FILES" | grep --color=never '.py$' || true)
|
||||
if [[ -f $f ]]; then
|
||||
ALL_FILES+=("$f")
|
||||
if [[ $f == *.py ]]; then
|
||||
PYTHON_FILES+=("$f")
|
||||
fi
|
||||
fi
|
||||
done < <(git ls-files -z openpilot)
|
||||
|
||||
run_tests "$ALL_FILES" "$PYTHON_FILES"
|
||||
# Include tooling, launchers, and the extensionless Git hook.
|
||||
SHELL_FILES=()
|
||||
while IFS= read -r -d '' f; do
|
||||
if [[ -f $f ]]; then
|
||||
SHELL_FILES+=("$f")
|
||||
fi
|
||||
done < <(git ls-files -z '*.sh' '*.bash' scripts/post-commit)
|
||||
|
||||
run_tests
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function fail {
|
||||
echo $1 >&2
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ function retry {
|
||||
"$@" && break || {
|
||||
if [[ $n -lt $max ]]; then
|
||||
((n++))
|
||||
sleep $delay;
|
||||
sleep "$delay";
|
||||
else
|
||||
fail "The command has failed after $n attempts."
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user