diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index c9d3c01fb..de15014dc 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/ubcsailbot/sailbot_workspace/dev:py-plotly +FROM ghcr.io/ubcsailbot/sailbot_workspace/dev:add-ament-mypy-v2 # Copy configuration files (e.g., .vimrc) from config/ to the container's home directory ARG USERNAME=ros diff --git a/.devcontainer/base-dev/base-dev.Dockerfile b/.devcontainer/base-dev/base-dev.Dockerfile index 08342ca59..3c3324d18 100644 --- a/.devcontainer/base-dev/base-dev.Dockerfile +++ b/.devcontainer/base-dev/base-dev.Dockerfile @@ -101,6 +101,7 @@ RUN apt-get update && apt-get install -y \ wget \ # Install ros distro testing packages ros-humble-ament-lint \ + ros-humble-ament-mypy \ ros-humble-launch-testing \ ros-humble-launch-testing-ament-cmake \ ros-humble-launch-testing-ros \ @@ -108,7 +109,7 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* \ && rosdep init || echo "rosdep already initialized" \ # Update pydocstyle - && pip3 install --upgrade pydocstyle + && pip3 install --upgrade pydocstyle mypy ARG USERNAME=ros ARG USER_UID=1000 diff --git a/.devcontainer/config/sailbot_workspace.code-workspace b/.devcontainer/config/sailbot_workspace.code-workspace index 2ff328e96..0ad4275b9 100644 --- a/.devcontainer/config/sailbot_workspace.code-workspace +++ b/.devcontainer/config/sailbot_workspace.code-workspace @@ -56,12 +56,14 @@ "DCMAKE", "deque", "devcontainer", + "dtype", "gaurav", "ints", "isort", "kmph", "mkdocs", "mongocxx", + "mypy", "noqa", "OMPL", "pallete", @@ -159,7 +161,6 @@ ], "flake8.interpreter": ["/usr/bin/python3"], // formatter: black and isort extensions - "python.formatting.provider": "none", "black-formatter.interpreter": ["/usr/bin/python3"], "black-formatter.args": [ "--line-length=99", @@ -172,6 +173,11 @@ "--ignore=/workspaces/sailbot_workspace/src/raye-local-pathfinding", ], "python.testing.pytestEnabled": true, + // type checker: mypy + "mypy-type-checker.args": [ + "--ignore-missing-imports", + ], + "mypy-type-checker.interpreter": ["/usr/bin/python3"], // c/c++ "[cpp]": { @@ -352,15 +358,13 @@ { "label": "lint_cmake", "detail": "Run lint on cmake files.", - "type": "ament", - "task": "lint_cmake", - "path": "src/", + "type": "shell", + "command": "LINTER=lint_cmake LOCAL_RUN=true .github/actions/ament-lint/run.sh", "problemMatcher": [ "$ament_lint_cmake" ], "presentation": { "panel": "dedicated", - "reveal": "silent", "clear": true } }, @@ -372,38 +376,45 @@ "problemMatcher": [], "presentation": { "panel": "dedicated", - "reveal": "always", "clear": true } }, { "label": "flake8", "detail": "Run flake8 on python files.", - "type": "ament", - "task": "flake8", - "path": "src/", - "commandOptions": "--exclude=src/virtual_iridium,src/raye-local-pathfinding", + "type": "shell", + "command": "LINTER=flake8 LOCAL_RUN=true .github/actions/ament-lint/run.sh", "problemMatcher": [ "$ament_flake8" ], "presentation": { "panel": "dedicated", - "reveal": "silent", + "clear": true + } + }, + { + "label": "mypy", + "detail": "Run mypy on python files.", + "type": "shell", + "command": "LINTER=mypy LOCAL_RUN=true .github/actions/ament-lint/run.sh", + "problemMatcher": [ + "$ament_mypy", + ], + "presentation": { + "panel": "dedicated", "clear": true } }, { "label": "xmllint", "detail": "Run xmllint on xml files.", - "type": "ament", - "task": "xmllint", - "path": "src/", + "type": "shell", + "command": "LINTER=xmllint LOCAL_RUN=true .github/actions/ament-lint/run.sh", "problemMatcher": [ "$ament_xmllint", ], "presentation": { "panel": "dedicated", - "reveal": "silent", "clear": true } }, @@ -413,6 +424,7 @@ "dependsOn": [ "lint_cmake", "flake8", + "mypy", "xmllint", ], "problemMatcher": [] @@ -438,10 +450,12 @@ { "id": "package", "type": "pickString", - "description": "Package to build", + "description": "Package to select", "options": [ "boat_simulator", + "controller", "custom_interfaces", + "global_launch", "local_pathfinding", "network_systems", ] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1a5883f89..35709ce3f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -82,6 +82,7 @@ "ms-python.vscode-pylance", "ms-toolsai.jupyter", "ms-vsliveshare.vsliveshare", + "ms-python.mypy-type-checker", "njpwerner.autodocstring", "stevejpurves.cucumber", "streetsidesoftware.code-spell-checker", diff --git a/.github/actions/ament-lint/run.sh b/.github/actions/ament-lint/run.sh index 813997678..8481ed582 100755 --- a/.github/actions/ament-lint/run.sh +++ b/.github/actions/ament-lint/run.sh @@ -20,6 +20,7 @@ function get_search_command { case ${LINTER} in lint_cmake) CMD='find ${VALID_SRC_DIRS_ARG} -type f \( -name "CMakeLists.txt" -o -name "*.cmake" -o -name "*.cmake.in" \)' ;; flake8) CMD='find ${VALID_SRC_DIRS_ARG} -type f -name "*.py"' ;; + mypy) CMD='find ${VALID_SRC_DIRS_ARG} -type f -name "*.py"' ;; xmllint) CMD='find ${VALID_SRC_DIRS_ARG} -type f -name "*.xml"' ;; *) error "ERROR: Invalid linter ${LINTER} specified in ament-lint action"; exit 1 ;; esac @@ -42,10 +43,22 @@ function lint { fi } -source /opt/ros/${ROS_DISTRO}/setup.bash -./setup.sh -cd src +if [[ $LOCAL_RUN != "true" ]]; then + source /opt/ros/${ROS_DISTRO}/setup.bash + ./setup.sh +fi # Exclude repos and files we don't want to lint -VALID_SRC_DIRS=$(ls | grep -v -e virtual_iridium -e docs -e raye-local-pathfinding -e polaris.repos) -lint ${VALID_SRC_DIRS} +VALID_SRC_DIRS=$(ls src | grep -v -e virtual_iridium -e docs -e raye-local-pathfinding -e website -e notebooks -e polaris.repos) +lint_errors=0 + +# Loop over each directory and lint it +for dir in $VALID_SRC_DIRS; do + echo "Run $LINTER on src/$dir" + lint src/$dir || { warn "WARNING: $LINTER errors in src/$dir, continuing with others"; lint_errors=1; } +done + +# Exit with an error if any lint command failed +if [ "$lint_errors" -ne 0 ]; then + exit 1 +fi diff --git a/.github/workflows/test_definitions.yml b/.github/workflows/test_definitions.yml index cdd90de57..1be7057d3 100644 --- a/.github/workflows/test_definitions.yml +++ b/.github/workflows/test_definitions.yml @@ -61,8 +61,8 @@ jobs: strategy: fail-fast: false matrix: - # ament_lint_common, except for copyright, cppcheck, cpplint, uncrustify, and pep257 - linter: [lint_cmake, flake8, xmllint] + # mypy and ament_lint_common, except for copyright, cppcheck, cpplint, uncrustify, and pep257 + linter: [lint_cmake, flake8, mypy, xmllint] name: ament_${{ matrix.linter }} runs-on: ubuntu-latest if: ${{ inputs.ros-ci }} diff --git a/src/global_launch/main_launch.py b/src/global_launch/main_launch.py index fb189bdad..115be3eca 100644 --- a/src/global_launch/main_launch.py +++ b/src/global_launch/main_launch.py @@ -3,7 +3,7 @@ import os from typing import List -from launch import LaunchDescription, LaunchDescriptionEntity +from launch import LaunchDescription from launch.actions import ( DeclareLaunchArgument, IncludeLaunchDescription, @@ -20,7 +20,9 @@ DEVELOPMENT_ROS_PACKAGES = ["boat_simulator", "local_pathfinding", "network_systems"] # Global launch arguments and constants. -ROS_PACKAGES_DIR = os.path.join(os.getenv("ROS_WORKSPACE"), "src") +ROS_PACKAGES_DIR = os.path.join( + os.getenv("ROS_WORKSPACE", default="/workspaces/sailbot_workspace"), "src" +) GLOBAL_LAUNCH_ARGUMENTS = [ DeclareLaunchArgument( name="config", @@ -65,7 +67,7 @@ def generate_launch_description() -> LaunchDescription: return launch_description -def setup_launch(context: LaunchContext) -> List[LaunchDescriptionEntity]: +def setup_launch(context: LaunchContext) -> List[IncludeLaunchDescription]: """Collects launch descriptions from all local launch files. Args: